您的位置 首页 java

Java-jvm-垃圾回收算法

一. 垃圾回收算法分类

1.1 标记-清除算法

最基础的算法,分为标记和清除两个阶段:首先标记出所有需要回收的对象,完成后统一回收掉所有被标记的对象。后续的算法都是基于此算法来改进的。
主要有两个缺点:

  • 效率问题:标记和清除的过程效率都不高。
  • 空间问题:标记清除之后会产生大量不连续的内存碎片,这样可能会导致以后再运行过程中需要分配大对象时无法找到足够的连续内存而不得不触发新的一次垃圾回收。

标记-清除算法图示-1

Java-jvm-垃圾回收算法

1.2 复制算法

为了解决效率问题,复制算法出现了。将内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存使用完了,就讲存活着的对象复制到另一块上面,然后把使用过的内存空间一次清理掉。
这样每次都是对一块内存进行回收,分配时也不考虑内存碎片等复杂情况。代价就是将内存缩小为原来的一半。
复制算法图示-2

Java-jvm-垃圾回收算法

现在的商业虚拟机都是采用这种收集算法来回收新生代。新生代中的对象98%是朝生夕死,所以并不需要按照1 : 1的比例来划分空间,而是分成一块较大的Eden区和两块较小的Survivor区。每次使用Eden区和其中的一块Survivor区。当回收时,将Eden区和Survivor中还活着的对象一次性拷贝到另外一块Survivor空间上,然后清除Eden区和用过的Survivor区。

1.3 标记-整理算法

复制收集算法在对象存活率较高时需要执行很多的复制操作,效率很低。所以在老年代里一般不能直接选用这种算法。
标记整理算法的标记过程和标记清除算法是一样,后续不是直接对可回收对象进行清除,而是将所有存活的对象都向一端移动,然后清理掉端边界以外的内存。
标记-整理算法图示-3

Java-jvm-垃圾回收算法

1.4 分代收集算法

当前商业虚拟机的垃圾收集都采用 “分代收集” 算法,这种算法就是根据对象的存活周期的不同将内存划分为几块。一般是把 java 对分为新生代和老年代,这样就根据各个年代的特点采用适当的收集算法。
在新生代中,每次垃圾回收时都有大量的对象死亡,只有少量的存活,所以选用复制算法,只需要付出少量的存活的复制成本就可以完成回收。
在老年代中,因为对象的存活率高、没有额外空间对它进行分配 担保 ,那就必须使用”标记-清理”,”标记-整理” 的算法进行回收。

二. 垃圾回收分析

2.1 为什么要堆分代?

为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能。你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我们要找到哪些对象没用,这样就会对堆的所有区域进行扫描。而我们的很多对象都是朝生夕死的,如果分代的话,我们把新创建的对象放到某一地方,当GC的时候先把这块存“朝生夕死”对象的区域进行回收,这样就会腾出很大的空间出来。

2.2 年轻代中的GC

JVM 把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1,为什么会是这个比例?
一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。
因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。

2.3 回收流程

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。

紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄 阈值 ,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

Java-jvm-垃圾回收算法

2.4 一个对象的一辈子

  • 我是一个普通的java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。
  • 有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。
  • 直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。
  • 在年老代里,我生活了20年(每次GC加一岁),然后被回收。

2.5 有关年轻代的JVM参数

1)-XX:NewSize和-XX:MaxNewSize
用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。

2)-XX:SurvivorRatio
用于设置Eden和其中一个Survivor的比值,这个值也比较重要。

3)-XX:+PrintTenuringDistribution
这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小。

4).-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold
用于设置晋升到老年代的对象年龄的最小值和最大值,每个对象在坚持过一次Minor GC之后,年龄就加1。

文章来源:智云一二三科技

文章标题:Java-jvm-垃圾回收算法

文章地址:https://www.zhihuclub.com/181017.shtml

关于作者: 智云科技

热门文章

网站地图