其他资料
目录
1.什么是java中的常量池?
常量池分为两个地方:运行时常量池 和 字符串常量池
运行时常量池: 字节码文件里面有个constant pool,存储着编译时生成的常量信息,在运行的时候,这些信息会被放在方法区中的运行时常量池中。
字符串常量池:存储一些字符串常量,位于堆区。
作用:避免重复创建相同的对象,节省内存并提高效率,
延伸→字符串常量池能否被垃圾回收?大量不同字符串不断intern()会导致内存溢出吗?
- 在Java 6之前,字符串常量池位于永久代(PermGen),由于永久代的垃圾回收效果不佳,如果字符串常量池中含有大量的字符串,很容易造成永久代溢出。
- 从Java 7开始,字符串常量池被挪到了堆中,堆空间一般比较大,且堆空间的回收效率较高,因此内存溢出的情况大大减少。
- 在Java 8中,字符串常量池仍然位于堆中,并且使用了元空间(Metaspace)代替永久代。元空间的内存大小取决于本地内存大小,这意味着字符串常量池的容量不再受限于JVM的固定内存大小,从而减少了内存溢出的风险
因此在java6之前,不断intern()不同字符串很容易就会造成内存溢出,java7以及java8之后这个情况就改善很多了。
2.你了解java中的类加载器吗?
类加载器负责在运行时动态加载类文件,确保类能够在 JVM 中被查找、加载和链接
作用:
加载类:从文件系统、网络或其他源中找到并加载.class 文件
链接类:验证类的字节码文件、准备分配内存,初始化变量、将符号引用转换成直接引用
初始化类:执行类的静态初始化块以及为静态变量赋值等操作
分类:
1.引导类加载器:负责加载 Java 平台核心库
2.扩展类加载器:加载 JDK 中 lib/ext 目录下的类库
3.应用程序类加载器:加载用户类路径(Classpath)下的类
4.自定义类加载器:继承 ClassLoader 类,可以在给定上下文的条件下加载类
工作流程:
1.请求加载:运行某个类时会首先查询类加载器是否已经加载了该类
2.双亲委派机制:类加载器优先委派给父加载器加载类,如果父加载器无法加载,则由当前类加载器进行加载(具体原理看以下博文第六题:每日速记10道java面试题04-优快云博客)
3.动态加载:当需要加载的类在某个路径下未找到时,类加载器将根据指定的类路径动态加载类
3.什么是java中的JIT?
JIT是一种运行时将字节码动态编译为本地机器代码的技术,主要用于提高 Java 程序的执行性能
原理:
字节码执行:当Java 程序运行时,JVM首先将.class 文件中的字节码加载到内存中,并通过解释器逐行执行这些字节码,
热点代码检测:在执行过程中,JJVM会监控代码的执行频率。频繁被执行的代码被视为“热点代码”JIT编译:一旦某段代码被识别为热点代码,JIT编译器会将这段字节码编译成本地机器代码,并存储在内存中
垃圾回收:JIT 编译的本地代码会保持在内存中,直到Java 程序运行结束或 JVM 执行垃圾回收
4.什么是java中的AOT?
Java 的 AOT(Ahead-Of-Time,预编译)是一种在程序运行之前,将Java 字节码直接编译为本地机器码的技术。
JIT 是在 Java 运行时将一些代码编译成机器码,而 AOT 则是在代码运行之前就编译成机器吗,也就是提前编译。
提前编译的好处是减少运行时编译的开销,且减少程序启动所需的编译时间,提高启动速度。
但同时它也有一些缺点:
做不到像JIT一样在动态运行时进行深度优化,所以在程序长时间运行下性能低于JIT。
AOT是针对特定平台下的,也是跨平台的灵活性。
5.你了解java的逃逸分析吗?
逃逸分析是JIT的优化技术,用于确定一个对象的作用范围,看是否会逃逸出当前方法或线程的作用范围。
如果对象不会逃逸,JVM可以实现标量替换、同锁消除等优化,减少内存分配开销和同步开销,从而提高应用的性能。
6.你了解java中有哪些引用吗?
java中有强引用、软引用、弱引用、虚引用。
强引用:最常见的引用类型,默认情况下,任何普通对象的引用都是强引用,有强引用引用到的对象,永远不会被垃圾回收器回收,即使内存不足也只是抛出异常但不会清除掉强引用对象。
软引用:内存不足即回收。内存不够用的时候回收,内存充足的时候不回收。可以用来做缓存。例如:内存空间够用的时候,从缓存取,不够的可以被回收,然后下次用的时候时候再从数据库取。
弱应用:发现即回收。ThreadLocal和weakhashmap都用了弱应用。ThreadLocal里面的map的key通过弱引用指向threadlocal对象。weakhashmap的键也是弱引用,当键不被其它强引用持有的时候,键值对会被自动释放。
虚引用:当垃圾回收器回收一个对象时,如果发现它还有虚引用,就会在回收对象之后,将虚引用加入到引用队列中,以通知应用程序对象的回收情况。用处:可以用来释放堆外内存。DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。
7.java中常见的垃圾回收器有哪些?
Serial Gc(串行垃圾回收器):单线程工作,适合小型应用,对内存的使用效率高,但在大内存下可能导致停顿时间较长。
Parallel Gc(并行垃圾回收器):利用多线程进行垃圾回收,能提高吞吐量,适用于多核处理器的环境。
CMS GC(并发标记清理垃圾回收器):通过并发执行标记和清理阶段来减少停顿时间,适合对响应时间敏感的应用,但可能产生记忆碎片。
G1GC(垃圾优先回收器):将堆划分为多个区域,优先回收对垃圾最多的区域,适合大内存的应用,能够提供可预测的停顿时间。
ZGC(Z垃圾回收器):一种低延迟的垃圾回收器,能够处理大内存和高并发场景,停顿时间非常短。
Shenandoah GC:类似于ZGC,旨在减少停顿时间,采用了并发和分区的方式。
8.java中如何判断对象是否为垃圾?
1.引用计数法(Reference Counting):
每个对象维护一个引用计数器,引用次数增加时,计数器加 1,减少时,计数器减1。当引用计数器为0时,说明该对象不再被引用,可以被回收。
优点:实现简单,实时性好。
缺点:无法处理循环引用的问题,两个对象互相引用时,引用计数器永远不会为 0。
2.可达性分析算法(Reachability Analysis):
Java 中垃圾回收主要采用可达性分析算法。通过从一组称为“GC Roots” 的对象出发,遍历所有可达的对象,凡是无法通过 GCRoots 到达的对象,均被视为垃圾。
优点:能够解决循环引用的问题。
缺点:需要消耗一定的资源进行标记。
9.为什么 Java 的垃圾收集器将堆分为老年代和新生代?
主要是为了提高垃圾回收的效率,java中对象的生命周期不是统一的,大多数对象的生命周期短,少部分对象的生命周期长,因此把生命周期短的对象分配到新生代,采用复制算法把存活下来的对象复制到Survive空间。把生命周期长的对象分配到老年代,采用标记-整理或标记-清除算法去回收对象。同时在新生代中存活多次GC的对象会晋升到老年代。
10.为什么java8移除了永久代引入了元空间?
相比于永久代,元空间具有更好的灵活性和扩展性,可以更好地满足不同应用程序的需求。 永久代的大小是固定的,当加载的类信息、常量池等数据超过了永久代的大小时,就会导致内存溢出。而元空间的大小可以根据需要进行调整,不再受到固定大小的限制。同时,元空间的数据可以存储在本地内存中,不再受到Java 堆大小的限制。因此,使用元空间替代永久代可以提高程序的灵活性和稳定性。