垃圾收集(GC)需要两件事:
如何找出需要回收的内存?
如何回收这些内存?
找出需要回收的内存
引用计数法(仅供学习)、可达性分析法、方法区回收条件
引用计数法
给对象头添加一个引用计数器,被引用计数器就加1,引用失效减1,计数器为0,对象不再被引用,可被回收。
该算法存在孤岛问题,如下图,也就是A对象引用了B,B引用了A,A、B的引用计数器均为1,但A、B已不再被其他地方引用,也无法被回收。
可达性分析法
通过GC Roots对象向下查找,走过的路径形成引用连,引用连到达不了的对象判定可回收。如下图的Obj5、Obj6、Obj7。
GC Roots对象包括:
-
虚拟机栈(栈帧中的本地变量表)引用的对象。
-
方法区中静态类属性引用对象。
-
方法区中常量引用的对象。
-
本地方法栈中JNI(Native方法)引用的对象。
引用:
-
强引用 Strong Reference:垃圾收集器永远不会回收被引用的对象。Object obj = new Object()。
-
软引用 Soft Reference:GC后内存不足,会将软引用的对象放入回收队列进行回收,还不足就内存溢出,一般用在缓存上。
-
弱引用 Weak Reference:GC直接回收。
-
虚引用 Phantom Reference:无法通过引用获取对象,对象回收时收到一个系统通知。
方法区回收
-
Java堆中不存在该类的实例
-
加载该类的ClassLoader被回收
-
该类的java.lang.Class没被引用,无法反射访问该类方法。
回收这些内存
标记-清除算法(Mark-Sweep)
标记出需要回收的对象,也就是上文提到的方法,然后统一回收标记的对象。
缺点:标记、清除效率都不高,且存在内存碎片化问题
由上图可见,回收后实际空闲的空间为5,但是如果申请4个内存,还是不足,因为这5个内存是碎片化内存,不连续。
复制算法(Copying)
将内存分为相同两块,当一块用完后,就将存活对象复制到另外一块,然后将原本那块内存回收。
缺点:空间成功太高,得留出来一半空间做保留区域。
标记-整理算法(Mark-Compact)
将存活对象向一端移动,然后清理掉边界以外的内存。
分代收集算法(Generational Collection)
根据对象存活周期,将内存划分为几块:新生代、老年代。然后根据每个年代特点,选用适合的收集算法。
新生代存活对象较少,一般选用复制算法,老年代对象存活率高,使用标记清理或者标记整理算法。
其中新生代还有个区域,TLAB(Thread Local Allocation Buffer),JVM默认给每个线程开辟了一个buffer区域,用来加速对象分配,线程私有。对象优先在TLAB上分配,对象过大会在Eden共享区分配。
新生代(又称年轻代)
新生代又分为Eden与2个Survivor,默认比例8:1:1,可通过参数-XX:SurvivorRatio设置,默认8.
每次只会使用一个Survivor。当Minor GC时,会将Eden与Survivor的存活对象复制到另外一个未使用的Survivor,然后清理Eden与Survivor。
老年代
进入老年代的条件:
-
大对象直接在老年代分配,-XX:PretenureSizeThreshold可设置,单位B。
-
长期存活的对象进入老年代,没进行一次Minor GC,年龄加1,默认15晋升老年代,-XX:MaxTenuringThreshold设置。
-
动态对象年龄判断:Survivor中相同年龄所有对象大小综合大于Survivor空间的一半,年龄大于该年龄的对象直接进入老年代。
-
在Minor GC时,Survivor空间不够用,多出来的对象就会进入老年代。
垃圾收集器
年轻代
-
Serial:GC时单线程,回收过程暂停一切用户线程,使用复制算法。
-
ParNew:GC时多线程,回收过程暂停一切用户线程,多CPU会降低STW时间。
-
Parallel Scavenge:GC时多线程,最求CPU吞吐量。
老年代
-
Serial Old:与Serial对应,单线程,使用标记-整理算法。
-
Parallel Old:与Parallel Scavenge对应,最求CPU吞吐量。
-
CMS:追求最短GC停顿时间。缺点较明显,CPU敏感,无法处理浮动垃圾,因为使用标记-清除算法,会有内存碎片,需要依赖Full GC进行整理。
G1:并行与并发、分代收集、空间整合、可预测停顿。建立多个大小相等的Region区域,然后判断回收价值,设定回收优先级。
每个Region不是独立的,因为存在对象引用。为避免GC时对所有Region扫描,使用了Remembered Set记录。
如下图,Region1中A对象引用Region2中的B对象,则Region2会在B建立引用时,在自己的Remembered Set中记录下Region1,然后Region2在GC时,就会带上Region1一起扫描。