链接: JVM垃圾收集之——怎样判定一个对象是不是垃圾
接上篇,介绍完怎样判定一个对象是不是垃圾之后,就该瞅一瞅垃圾是怎样回收的了
首先我们要知晓,垃圾收集是建立在两个分代假说之上的:
①弱分代假说:绝大多数对象都是朝生夕灭的
②强分代假说:熬过越多次垃圾收集的对象就越难消亡
收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄分配到不同的区域中存活。
给出收集的几个名词,以便于大家看文章:
- 部分收集(Partial [ˈpɑːʃl] GC):指目标不是完整后记整个Java堆的垃圾收集。
其中又细分为:- 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
- 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
请注意“Major GC”这个说法现在有点混淆,在不同资料上常有不同所指, 读者需按上下文区分到底是指老年代的收集还是整堆收集 - 混合收集(Mixed GC):指目标是收集整个新生代及部分老年代的垃圾收集,目前只有G1垃圾收集器有这种行为。
- 整堆收集(Full GC):收集整个Java堆和可能会触发方法区的垃圾收集。
标记—清除算法
首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象,也可以反过来标记存活的对象,然后回收违背标记的对象。
第一步、找到内存中需要被回收的对象,并且把它们标记出来。
此时堆中的所有对象都会被扫描一遍,从而才能确定需要回收的对象,比较耗时。
第二步、清除掉被标记需要回收的对象,释放出对应的内存空间。
注意:这里所谓的清除并不是真的置空,而是把需要清除对象的地址回收到空闲的地址列表里,下次有新对象需要加载时,直接使用这些地址即可,也就相当于一个覆盖的过程。(这是别的八股文所没有介绍的)
缺点:
(1)标记和清除两个过程都比较耗时,效率不高 。
(2)会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记—复制算法
将可用内存分为两块,每次只用一块,用完时将这一块还存活的对象复制到另一块上,然后一次性清空这一块。
缺点:空间利用率降低,浪费空间。
标记—整理算法
我们发现第一种算法会导致碎片化,第二种算法又浪费内存空间,于是出现了第三种:
第一步、标记(标记的过程仍然与“ 标记-清除”算法一样),但是后续步骤不是直接对可回收对象进行清理。
第二步、将所有的存活对象压缩到内存的一端,按顺序排放之后,清理边界外所有的空间。
优点:没有碎片化,也不浪费内存空间。
缺点:但是效率比较低,甚至比标记复制更低。
分代收集
从垃圾收集算法到如今,只有这三种垃圾回收器算法,如果算上分代收集算法就是四种。
每种垃圾回收算法都有缺点,要是把你的内存当成一整块的时候,无论你采用什么样的算法,都有它自己的问题,所以垃圾回收器非常聪明,他是用这三种算法综合运用,然后同时对内存进行了划分,产生了各种各样的不同的垃圾回收器。
其实这里背后是有分代收集的理论作为支撑的,开发者记录了所有的对象的生命周期,
其一、发现绝大多数对象都是“寄蜉蝣于天地,渺沧海之一粟”。(弱分代假说)
其二、熬过越多次垃圾收集过程的对象就难以消亡(强分代假说)。
这两个现象奠定了垃圾收集器的一致的设计原则:收集器应该将内存划分出不同的区域,然后将回收的对象依据其年龄(年龄既是对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。如果一个区域中大多数对象都是朝生夕死的,难以熬过垃圾收集器过程,那么将它们集中在一起,每次回收只关注保留下存活的对象而不是去标记那些大量的将要被回收的对象,就能以最小的代价会回收到大量的空间;如果剩下的都是难以消灭的对象,那么把他们集中在一起,虚拟机就可以使用较低的频率来回收这个区域,就兼顾了时间开销和内存空间的有效利用。
Java虚拟机设计者一般会把Java堆分为新生代(Young Generation)和老年代(Old Generation)
在新生代中,每次垃圾收集 时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。
动态对象年龄判定:(虚拟机并不会永远地要求对象的年龄都必须达到MaxTenuringThreshold
才能晋升老年代,如果Survivor
空间中相同年龄的所有对象的大小总和大于Survivor
的一半,年龄大于或等于该年龄的对象就可以直接进入老年代),空间分配担保,老年代的连续空间大于(新生代所有对象的总大小或者历次晋升的平均大小)就会进行minor GC,否则会进行full GC。
不同区用什么算法:
Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
Old区:标记清除或标记整理(Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理)