Java内存管理之用什么方式回收垃圾、管理内存呢?垃圾回收算法有哪些?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_34122324/article/details/84898046


在这里插入图片描述


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 后仍然存活下来的对象。

工作原理

  1. 首先,Eden区最大,对外提供堆内存。当 Eden 区快要满了,则进行 Minor GC,把存活对象放入 Survivor A 区,清空 Eden 区;
  2. Eden区被清空后,继续对外提供堆内存;
  3. 当 Eden 区再次被填满,此时对 Eden 区和 Survivor A 区同时进行 Minor GC,把存活对象放入 Survivor B 区,同时清空 Eden 区和Survivor A 区;
  4. Eden区继续对外提供堆内存,并重复上述过程,即在 Eden 区填满后,把 Eden 区和某个 Survivor 区的存活对象放到另一个 Survivor 区;
  5. 当某个 Survivor 区被填满,且仍有对象未被复制完毕时,或者某些对象在反复 Survive 15 次左右时,则把这部分剩余对象放到Old 区;
  6. 当 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后进入老年代的平均大小大于老年代的可用内存。

总结:新生代 采用 回收 机制,老年代 采用 标记整理 机制

猜你喜欢

转载自blog.csdn.net/baidu_34122324/article/details/84898046
今日推荐