jvm总结之二:回收策略

前言

我们知道垃圾回收的主要内存区域是堆和方法区,因为这两块区域的内存分配相对于其他区域具有不确定性,而分配内存一般有两种方法,即指针碰撞和空闲列表

堆内存

在堆内存中主要分为年轻代和老年代(具体后面再说),先说一下年轻代
Alt text
如图,主要分为Eden(对象都会在该区域内创建)同时也会在s0或s1中随机一个进行创建,经过一次GC后在s0和s1中使用复制算法保留存活下来的对象,那么它是如何复制的了
Alt text
Alt text
大概流程就是将可回收的垃圾对象进行标记,然后复制存活的对象到另一块空白区域,依次类推
很显然这种复制方式很浪费空间,但是年轻代是生命周期很短的,换句话说,如果年轻代不够放了就会放到老年代,一般规定经过15次GC后的年轻代就会进入老年区(可通过-XX:MaxTenuringThreshold=15设置多少次为老年代),而老年区一般存放存活时间比较久的对象,通过标记-清除-整理进行垃圾回收,如果老年代内存不足就会触发FULL GC.默认是占用了68%后收集(可通过-XX:CMSInitiatingOccupancyFraction=68设置),该GC非常影响性能

可达性分析

我们知道垃圾回收算法可自行判断一个对象是否已经生命周期达到结束,从而将其视为垃圾进行回收。那么在此算法出现之前,出现过可达性分析算法进行垃圾回收
算法的基本思想就是通过一系列的称为“GC Roots”的对象作为起始点,从这些起始点开始向下搜索,所走过的路径称为引用链,如果一个对象到GC Roots没有任何引用链,那么这个对象是不可用的,就是说,程序中没有谁引用了这个对象,所以可以说从根节点到叶子结点是不可达的
而以下对象可作为GC Roots对象:
虚拟机栈(栈帧中本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(也就是native本地方法)引用的对象

现在我们已经知道了通过可达性分析判断对象是否还可用,但是是不是不可达的对象都是不可用呢?答案是未必。原因在于要宣告一个对象的死亡,需要两次标记(为什么需要两次标记呢?原因是如果一个对象没有与GC Roots结点相连,就会被第一次标记,而如果对象覆盖了finalize方法,并且在finalize方法中与某个对象建立了引用关系,那么第二次标记会失败,那么这个对象就会被移出“即将回收”的对象列表,移出之后这个对象就“活”了下来,如果在finalize方法中这个对相关仍然没有与一个对象建立引用关系,那么这个对象就真正死亡了)。

回收方法区中的对象

方法区中的永久代的垃圾回收主要为两部分: 废弃常量和无用的类
废弃常量是指没有任何对象引用常量池中的某个对象,那么这个对象就会被回收,而对于常量池中的垃圾回收只要进行一次标记就可以进行判断
无用的类需要满足以下三个条件才可以宣判一个类的“死刑”:
1.该类的所有实例都已经被回收,也就是Java堆中不存在该类的实例
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

热评文章