微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JVM垃圾回收机制

我们使用Java的时候不会考虑垃圾对象的释放问题,因为在java程序中有垃圾回收机制帮我们管理内存(叫GC,gabbage collection),垃圾收集器会在适当的时候对没用的对象进行整理与回收。下面简单整理下jvm 中GC相关知识点来学习。

一、什么时垃圾对象

        我们知道gc回收垃圾对象,那么什么是垃圾对象,什么样的对象才会被视为垃圾从而被处理掉,如何判断对象可被回收?

1.引用计数法

        引用计数法的算法思路:给对象增加一个引用计数器,每当对象增加一个引用计数器+1,失去一个引用-1,所以当计数器是0的时候对象就没有引用了,就会被认为可回收垃圾

        但是,在主流的JVM中没有选用引用计数法来管理内存,最主要的原因是引用计数法无法解决对象的循环引用问题。

2.可达性分析算法

        Java并不采用引用计数法来判断对象是否是垃圾,而采用“可达性分析”来进行判断

        可达性分析的算法思路:通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(即从 GC Roots 到这个对象不可达)时,证明此对象不可用(视为垃圾)。以下图为例:

 虽然Object5-7是有联系的,但是他们到gc roots不可达,因此他们会被判定为可回收的垃圾对象

java中可作为GC Roots的对象包含以下几种:

        1.虚拟机栈(栈帧中的本地变量表)中引用的对象

        2.方法区中静态引用的对象

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

        4.本地方法栈中(Native方法)引用的对象

二、内存空间的回收

上面说了如何判定对象是否可以被回收,接下来就要看如何来回收,回收的方法是什么,一般现代的虚拟机有以下的回收算法,他们的具体适用场景也不一样,下面进行简单的整理归纳。

1、标记清除算法。

        该算法直接对要回收的对象进行标记,在GC执行时,直接将标记的对象进行回收。这个算法相对来说实现起来比较简单,执行起来速度也快。但是,由于要回收的对象之间的内存块是不连续的,在进行回收之后,会造成很多空间碎片,不便于大对象的创建。

2、复制算法。

        复制算法的做法和标记整理的算法不同,它是直接将还有用的对象复制到另一个区域,剩下的就是无用的对象,可以直接被回收。复制算法针对的情形是:回收的对象远远多于存活的对象,这种情况在大多数的新生代区域(该区域所有对象都没有经历过垃圾回收)是满足的。

一般来说,虚拟机会将堆中的内存划分为8:1:1(这个比例的确定是因为大多数时候在新生代进行垃圾回收时,回收对象和存活对象大小之比大概为9:1)的大小区域,用于执行垃圾回收算法,一般将它们分别命名为eden和survior区域,看下面示意图:

 

在上面的内存划分示意图中,eden区域存储的是刚刚new出来的对象(没有经历过垃圾回收),而survior中的一个区域是作为上一次(或更早之前)存活对象的场所,另一个区域是用作回收时的中间站。在进行GC时,会将eden和相应的survivor区域中的存活对象统一复制到另一个作为中间站的survivor中,然后清除剩余的对象,重复这个过程。(当然,survior中的存活对象有可能会不断增多,这是,当有些存活对象年龄足够大时,会被移到老年区,老年区相对与新生代区域垃圾回收的频率会低很多。)当然,如果在新生代出现了存活对象大于10%的情况(例如在某个时刻有一个很大的对象,它在第一次GC时不能进行回收)时,survivor中的内存不够用了,GC程序会提前让对象进入老年区(也就是会采用老年代的区域进行对象的复制),以保证内存空间的充足。所以,很多时候,大对象会直接进入老年区,这样可以防止大对象的频繁复制损耗性能

  显然,复制算法有效解决了内存碎片化的问题,但是,复制对象针对的是存活对象一般保持在10%左右的新生代区域罢了,在老年代区域中,存活对象会更多,并不适合采用该算法,一般会采取下面介绍的标记整理算法。

3.标记整理算法

复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法。
针对老年代的特点,提出了一种称之为“标记-整理算法”。标记过程仍与“标记-清除”过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象向一端移动,然后直接清理掉端边界以外的内存。流程图如下:

4.分代收集算法

        当前JVM垃圾收集都采用的是"分代收集(Generational Collection)"算法,这个算法并没有新思想,只是根据对象存活周期的不同将内存划分为几块。
        一般是把Java堆分为新生代和老年代。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记-清理"或者"标记-整理"算法。

面试题: 请问了解Minor GC和Full GC么,这两种GC有什么不一样吗?

        Minor GC又称为新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。
        Full GC 又称为老年代GC或者Major GC : 指发生在老年代的垃圾收集。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐