JVM 垃圾回收器主要完成的事情有:
- 哪些对象需要回收 ?
- 什么时候回收 ?
- 如何回收 ?
1、JVM 如何判断哪些对象需要回收?以及如何回收?
1.1 、如何判断对象为垃圾
- 引用计数法:在对象中添加一个引用计数器,每当有一个地方引用它的时候计数器加一,引用失效的时候计数器减一。当计数器为零的时候表示该对象可以被回收。但引用计数法无法解决对象之间的循环依赖问题。
- 可达性分析算法:其通过GC Roots 的根对象作为起始节点,从这些结点开始,根据引用关系向下搜索,搜素过程所走的路径为 “引用链”,如果某个对象带GC Roots 间没有任何引用链相连,则代表其是可以被回收的。
GC Roots 对象包括:
- 虚拟机栈中的引用对象
- 方法区中静态属性引用的对象
- 方法区中的常量引用对象
当一个对象它不可达的时候它是非死不可吗?其实并不是如果你重新了其finalize()方法的话,在垃圾回收之前会先调用此方法这时你可以做进一步的挽救。但是任何一个finaline()方法都只会被系统调用一次。
垃圾回收它只会发生在堆中吗?其实并不是方法区中也存在方法回收。其主要回收的数据有两部分:废弃的常量和不在使用的类。回收不在使用的类有如下条件:
- 该类的所有实例对象都已经被回收了
- 加载该类的类加载器已经被回收
- 其对应的Class 对象在任何地方没有被引用,无法在任何地方通过反射访问该类的方法;
1.2、常见的垃圾回收算法
- 标记-清除算法
其主要思路是标记要回收的垃圾,然后进行回收。但他存在两个问题:1、执行效率不稳定,如果存在大量的垃圾这时必须要进行大量的标记和清除。2、存在内存空间碎片
- 标记-复制算法
其是将内存按容量划分为两个大小相同的两块,每次只使用其中一块,当进行垃圾回收的时候则把存活的对象复制到另一块上,其缺点是可用内存缩小为原来的一半,但其避免了内存碎片问题。还有就是对于新生代来说对象的存活基本是朝生夕灭的,所以标记复制算法适用于新生代。
- 标记-整理算法
标记整理算法其和标记清除算法一样,但其差别是其会整理存活对象对其进行移动,正是这样在垃圾回收的时候其必须要暂停所有的用户线程,从而导致Stop The World。
1.3、常见的垃圾收集器
- Serial 收集器:其是一个单线程工作的收集器,这代表在进行垃圾垃圾回收的时候只有一个线程来进行回收,这时所有的用户线程都要停止。(新生代采用复制算法,老年代采用标记整理算法)
- ParNew 收集器:ParNew 设计它其实就是 Serial收集器的多线程并行版本,在单核的情况下ParNew 收集器不一定不 Serial 收集器好,因为ParNew 收集器多线程会浪费不必要的线程切换。
- CMS 收集器:
- G1 收集器:
2、内存分配与回收策略
2.1 、对象优先分配在Eden区
新建的对象优先分配在Eden 区,HotSpot 虚拟机提供了 -XX:+PrintGCDetails 这个收集器日志参数。在运行时我们也可以通过 -Xms20M -Xmx20M -Xmn10M 这 三个参数来限制Java 堆内存大小。
2.2 、大对象直接进入老年代
大对象需要大量来连续内存空间的Java对象,在分配空间时,它容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置它们,且当复制对象时,大对象就意味着高额的内存复制开销。HotSpot 虚拟机提供了 -XX:PretenureSizeThreshold 参数用于指定当大对象大于该值时对象直接进入老年代。这样做的目的是避免大对象在Eden区和Survivor区之间来回复制,产生大量的复制操作。 注:-XX:PretenureSizeThreshold 参数只对 Serial 和 ParNew 两种新生代收集器有效
2.3、长期存活的对象将进入老年代
对象通常在Eden区诞生,如果经理过一次Yong GC之后,并且能被 Survivor 区容纳的话,该对象就会被移到Survivor 空间中,并将其年龄设置为 1 岁,对象在Survivor 区每存活一次其年龄增加一岁,当前年龄增加到一定程度(默认为15)就会进入老年代。其可以通过参数 -XX:MaxTenuringThreshold 设置。注:其年龄存储在对象头中
2.4、动态对象年龄判断
在HotSpot 虚拟机中并不是要求所有对象都必须达到 -XX:MaxTenuringThreshold 才能晋升老年代,如果在Survivor 空间中相同年龄所有对象总和大于Survivor区的一半,年龄大于或者等于该年龄的对象就可以直接进入老年代。
2.5、空间分配担保
在发生Yong GC的时候,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这条件成立则可用保证这次YongGC是安全的。如果不成立,则虚拟机会查看 -XX:HandlePromotionFailure 参数的设置是否允许担保失败,如果允许则会继续检查老年代最大连续可用空间是否大于历次晋升到老年代的平局大小,如果大于则冒险尝试一下Yong GC ,如果小于或者设置不允许冒险的话则触发Full GC 之后还不行的话则抛出异常。