文章目录
- 1. 什么是垃圾?
- 2. 标记-清理(Marked-Sweep)
- 3. 标记-整理(Marked-Compact)
- 4. 复制(Copying)
- 6. Java分代回收机制
- 6.1 新生代-复制 回收机制(Minor GC,把内存按 8:1:1 分)
- 6.2 老年代-标记整理 回收机制(Full GC)
- 6.3 总结
1. 什么是垃圾?
所有 GC Roots 不可达的对象都称为垃圾,参考下图,黑色的表示垃圾,灰色表示存活对象,绿色表示空白空间。
2. 标记-清理(Marked-Sweep)
所谓“标记”就是利用可达性遍历堆内存,把“存活”对象和“垃圾”对象进行标记,得到的结果如上图;
第二步,既然“垃圾”已经标记好了,那我们再遍历一遍,把所有“垃圾”对象所占的空间直接 清空 即可。标记-清理 方案,简单方便 ,但是容易产生 内存碎片。
3. 标记-整理(Marked-Compact)
既然上面的方法会产生内存碎片,那好,我在清理的时候,把所有 存活 对象扎堆到同一个地方,让它们待在一起,这样就没有内存碎片了。这两种方案适合 存活对象多,垃圾少 的情况,它只需要清理掉少量的垃圾,然后挪动下存活对象就可以了。
4. 复制(Copying)
这种方法比较粗暴,直接把堆内存分成两部分,一段时间内只允许在其中一块内存上进行分配,当这块内存被分配完后,则执行垃圾回收,把所有 存活 对象全部复制到另一块内存上,当前内存则直接全部清空。
6. Java分代回收机制
Java 里是如何选择利用这三种回收算法呢?
常规的 Java 堆至少包括了 新生代 和 老年代 两块内存区域,而且这两块区域有很明显的特征:
- 新生代:存活对象少、垃圾多
- 老年代:存活对象多、垃圾少
结合新生代/老年代的存活对象特点和之前提过的几种垃圾回收算法,可以得到如下的回收方案:
6.1 新生代-复制 回收机制(Minor GC,把内存按 8:1:1 分)
依次取名为 Eden、Survivor A、Survivor B 区,其中 Eden 意为伊甸园,形容有很多新生对象在里面创建;Survivor区则为幸存者,即经历 GC 后仍然存活下来的对象。
工作原理
- 首先,Eden区最大,对外提供堆内存。当 Eden 区快要满了,则进行 Minor GC,把存活对象放入 Survivor A 区,清空 Eden 区;
- Eden区被清空后,继续对外提供堆内存;
- 当 Eden 区再次被填满,此时对 Eden 区和 Survivor A 区同时进行 Minor GC,把存活对象放入 Survivor B 区,同时清空 Eden 区和Survivor A 区;
- Eden区继续对外提供堆内存,并重复上述过程,即在 Eden 区填满后,把 Eden 区和某个 Survivor 区的存活对象放到另一个 Survivor 区;
- 当某个 Survivor 区被填满,且仍有对象未被复制完毕时,或者某些对象在反复 Survive 15 次左右时,则把这部分剩余对象放到Old 区;
- 当 Old 区也被填满时,进行 Major GC,对 Old 区进行垃圾回收。
6.2 老年代-标记整理 回收机制(Full GC)
老年代一般存放的是存活时间较久的对象,所以每一次 GC 时,存活对象比较较大,也就是说每次只有少部分对象被回收。因此,根据不同回收机制的特点,这里选择 存活对象多,垃圾少 的标记整理 回收机制,仅仅通过少量地移动对象就能清理垃圾,而且不存在内存碎片化。
6.3 总结
Minor GC 是新生代Copying算法。MinorGC触发条件:
(1)当Eden区满时,触发Minor GC。
Full GC 的老年代,采取的Marked-Compact。Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行。
(2)老年代空间不足。
(3)方法区空间不足。
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存。
总结:新生代 采用 回收 机制,老年代 采用 标记整理 机制