深入理解Java虚拟机(三)—— 垃圾收集器与内存分配策略

一、GC回收区域

        前面两篇博客了解到,JVM的内存模型中程序计数器、虚拟机栈、本地方法栈这三哥内存区域随线程的生命周期结束而消亡,栈中存储的栈帧分配多少内存大体上是在编译期就已经确定的,因此这几个区域的内存与回收都是明确的,当方法结束或者线程结束时,内存自然跟着回收了。

        与栈不同,Java堆和方法区有着不确定性:一个接口的多个实现类需要的内存可能会不同,一个方法执行不同条件分支需要的内存也不同,这些是在编译期无法确定的,只有在运行期间才能得知,这部分的分配和回收是动态的,GC所关注的也是这部分内存的回收管理。

二、回收对象区分

        既然已经明确了GC回收的主要区域是方法区,方法区的回收条件十分苛刻,且回收成效甚微,在这里只着重讨论GC在堆中的活动,前面了解到堆中主要存放创建的Java对象,那么GC在回收前要做的第一件事就是确定哪些对象还有存在的价值,哪些对象已经没用了,而GC区分对象基于两种方式:引用计数法可达性分析

2.1 引用计数法

        在对象中添加一个引用计数器,每当有地方引用它时,计数器加一;引用失效时,计数器减一。计数器为0时,代表该对象不再被使用。

        优点:简单高效

        缺点:占用了额外的内存空间进行计数,难以解决对象间的循环引用问题,如下:

... ...

public Object instance = null ;

public void test(){

    ObjDemo objA = new ObjDemo();

    ObjDemo objB = new ObjDemo();

    objA.instance = objB;

    objB.instance = objA;

}

... ...

2.2 可达性分析算法

        目前,主流的虚拟机都是通过可达性分析算法来判定对象是否存活。

        基本思路是通过“GC Roots”根对象作为起始点,根据引用关系向下搜索,搜索过程中所走过的路径称为“引用链”(Refrence Chain),如果一个对象到GC Roots间没有任何的引用链,则表示该对象不再被使用。


图 2-1 可达性分析

        固定可以作为GC Root的对象包括:

        (1)虚拟机栈中引用的对象,如线程被调用的方法堆栈中使用到的参数、局部变量、临时变量。

        (2)在方法区中类静态属性引用的对象,如引用类型静态变量。

        (3)方法区中常量引用的对象

        (4)Native方法引用的对象

        (5)基本数据类型对应的Class对象

        (6)异常对象,如NullPointerException

        (7)同步锁持有的对象

        (8)程序执行中,临时加入的对象

三、强、弱、软、虚引用

        无论是引用计数还是可达性分析,本质都是判断对象是否还存在引用关系。Java对引用划分为四类:强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。四种强度逐级减弱。

        (1)强引用:无论何种情况下,只要强引用关系存在,GC就不会回收掉被引用的对象。如下:

public class Demo {

        public static void main(String[] args) {

                StronglyRefDemo stronglyRefDemo = new StronglyRefDemo();

                System.out.println("输出:"+stronglyRefDemo);

                System.gc();

                System.out.println("gc后:"+stronglyRefDemo);

        }

}

        运行结果如图:

图 3-1 强引用案例运行结果

        (2)软引用:用来描述一些还有用,但非必须的对象。被软引用关联的对象,在系统即将发生OOM异常前,会被纳入GC回收范围进行第二次回收,如果被软引用关联的对象回收后,依然没有足够的内存空间才会抛出OOM异常。

软引用通过 SoftReference<Object> softReferenceObj = new SoftReference<>(new Object()); 创建。

        (3)弱引用:中来描述非必须的对象,强度比软引用还弱。被弱引用关联的对象只能存活到下一次GC发生为止。无论当前内存空间是否足够,都会回收掉这些被弱引用关联的对象。

弱引用最典型的是TreadLocal会引起内存泄露,感兴趣的朋友可以去查阅一下。

弱引用通过WeakReference<Object> objectWr = new WeakReference<>(new Object());创建。

        (4)虚引用:虚引用只有一种功效,就是为了能在对象被GC回收时收到一个通知。一个对象是否有虚引用关系,对其生命时间完全构不成影响,是四种引用当中最弱的一种引用关系。

虚引用创建方式:

ReferenceQueue<Object> queue = new ReferenceQueue<>();

PhantomReference<Object> oneObjectPr = new PhantomReference<>(new Object(),queue);

四、自我救赎之finalize

        GC可达性分析判定为待回收的对象,并不意味着该对象必须被回收,真正被回收至少要经历两次标记的过程:

        (1)可达性分析后发现没有有GC Roots之间有引用链,将第一次标记该对象。

        (2)检查对象是否需要执行finalize()方法,如果该对象没有覆盖finalize()方法,或者finalize()已经执行过了,虚拟机会将这两种情况视为“没有必要执行”。如果对象被判定需要执行finalize()方法,该对象会进入一个名为F-Queue的队列中,虚拟机随后会建立一个低调度优先级的Finallizer线程去执行该对象的finalize()方法。finalize()方法是待回收对象的最后一次救赎,只要待回收对象在finalize()中重新与引用链上的任何一个对象建立关联,就能拯救自己被回收的命运,否则,就会被GC回收掉。

        一个对象的finalize()方法最多只会被系统调用一次。自我救赎复活案例代码如下:

图 4-1 finalize自我拯救

    执行结果如下:

图 4-2 finalize自我拯救代码运行结果

五、垃圾收集算法

5.1 标记-清除算法

        和它的名字一样,算法被分为“标记”和“清除”两个阶段:

        (1)标记所有需要回收的对象;

        (2)标记完成后,统一回收掉被标记的对象。

        标记-清除算法是最基础的算法,后续的算法也基本上是对标记-清除算法的缺点进行改造:

        优点:(1)算法简单   (2)适用于存活对象比较多的情况;

        缺点:(1)执行效率不稳定,标记和清除过程的执行效率随着对象的增加而降低;(2)标记清除后,会产生内存空间碎片,可能会导致后面的大对象无法找到足够的连续内存而引发一次GC。

图 5-1 标记-清除算法

5.2 标记-复制算法

        目的:解决标记-清除算法在面对大量回收对象时回收效率低下。

        在“半区复制”算法基础上,标记-复制采用了更优化的半区复制分代策略

        (1)把新生代分为一块较大的Eden空间和两块较小的Survivor空间(From和To),每次分配内存只使用Eden和其中一块Survivor;

        (2)垃圾收集时,将Eden和From Survivor中仍然存活的对象一次性复制到另外一个To Survivor空间上;

        (3)直接清理Eden和From Survivor空间。

        (4)From Survivor 和 To Survivor 互换。

图5-2 标记-复制算法

        至此,标记-复制算法的优缺点显而易见:

        优点:不会产生内存空间碎片;

        缺点:(1)对象存活率较高时,大量的复制操作会降低效率 (2)空间浪费

5.3 标记-整理算法

        目的:针对标记复制算法的缺点,并不适合在老年代当中使用,老年代当中的大对象复制会极大影响效率,且复制需要更大的内存空间去完成,对此引出了标记-整理算法

        (1)标记:过程与“标记-清除”算法一致;

        (2)整理:所有存活对象向内存空间一端移动;

        (3)清除:清理掉边界以外的内存。

图 5-3 标记-整理算法
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容