1.哪些内存需要回收?
1.1.一次标记(可达性分析算法)
有一系列“GCRoots”起点,从这些点开始向下搜索,走过的路径称为“引用链”。若一个对象没有任何引用链可到达GC Roots,那么该对象就是不可用的,即使该对象还与其他对象相关联。
GC Roots类型:
① 虚拟机栈中引用的对象
② 方法区中类静态属性引用的对象
③ 方法区中常量引用的对象
④ 本地方法栈中引用的对象
经可达性分析算法所标记出的对象,会进行一次标记,但是这并不意味着非死不可,要真正搞死一个对象,至少要经历两次标记。
1.2.二次标记(死缓)
经可达性分析算法所标记出的对象,会进行一次筛选(根据finalize方法)。若经过筛选,判定可回收,那么就会立即回收;若判定没有必要回收,那么就将对象放入F-Queue队列中,进行二次筛查。
二次筛查会执行对象的finalize()方法。若对象在这个过程重新与引用链上的任何一个对象建立关联,那么该对象就会从回收集合中移除。否则,对象会被回收。
2.什么时候回收?
(1)程序调用System.gc时可以触发;
(2)系统自身来决定GC触发的时机:
系统判断GC触发的依据:根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程。
3.怎么回收?
为了让内存回收更加高效,目前主流的JVM(HotSpot)采用的是分代收集算法,从Sun JDK 1.2开始对堆采用了分代管理方式,如下图所示:
3.1新生代(Minor GC/Young Generation)
对象在被创建时,内存首先是在年轻代进行分配(注意,大对象可以直接在老年代分配)。当年轻代需要回收时会触发Minor GC(也称作Young GC)。
实际情况下,98%的对象都是存活率为0,所以可以将运行时的内存(即第2点中的第一块内存)所占用的比例调整大一点。年轻代由Eden Space和两块相同大小的Survivor Space(又称From Space和To Space)构成,Eden区和Servior区的内存比为8:1:1。所以在新生代中采用复制算法,这样可用内存就占到新生代总容量的90%。
当S1空间满了,则会将对象移到老年代。而且复制算法会对每次存活下来的对象计数,如果存活次数到达一定阈值(默认15),则也会将对象移到老年代。
3.2老年代
与新生代不同的是,老年代对象存活率较高,这时采用复制算法,效率将会变低。老年代采用标记-整理算法:
- 标记出所有需要回收的对象
- 所有存活的对象都向一端移动
- 清理边界以外的内存
3.3永久代
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。
请参考下Java8:从永久代到元数据区
(译者注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)
参考:
https://blog.csdn.net/fyyyr/article/details/79389222
https://blog.csdn.net/weixin_39788856/article/details/80388002