gc机制执行流程 java jvm完整的gc流程_gc机制执行流程 java

堆区是用来存储new出来的对象的,当对象填充满堆区后,就会导致内存爆掉,程序就GG了。

就需要科学的进行GC:

首先需要判断这个对象是否应该被删除,如果应该被删除,那么需要将这个对象清理掉。

判断的标准:GCRoot(一般是指被栈上的直接或间接引用、本地方法栈直接或间接引用的对象、方法区的j静态static变量或常量直接或间接引用的对象)

和GCRoot没有相连的关系的就可以删除。

清理堆区对象的思路:

  • 标记-清理(对象后打标,如果应该被删除就标记一下,然后再扫描一次,把含有标记的对象删除掉)

但是标记清理算法的缺点是产生内存碎片。【就像衣服打洞】

  • 标记-整理(清除过后,后面的对象补上来)

这样就减少了内存的碎片,但是代价太大,所有对象都需要前移。

  • 复制算法:

gc机制执行流程 java jvm完整的gc流程_gc机制执行流程 java_02

复制算法将内存1分为2.在1区上创建的对象标记是否需要删除,等到快满的时候,不是直接将这个对象删除,而是往2区进行复制,

需要删除的就不复制过来了,不需要删除的这些就紧凑的复制过来。这样就避免了内存碎片问题和开销也不大。

但是其缺点就是需要2倍的内存。


实际GC过程:

gc机制执行流程 java jvm完整的gc流程_堆区_03

将堆区进行划分为:年轻代、老年代。

对于年轻代又进行划分为3个区:E区、S(surive)0区、S1区。

而老年代就只有1个区。

整个过程在进行new对象的时候它其实是产生在E区的。当E区快满了的时候,就会触发Young区的GC,采用的是复制算法。会对需要删除的对象上打标记,不需要删除的对象依次复制到S0区。

这里有2点疑问:

  1. E区比S区大
  2. 有2个S区

E区比S区大的原因是对象都有个特点:“朝生夕死”,很容易就夭折了,幸存下来的比较少。比例大概是1:1:8

需要2块S区是因为需要交替工作的,即E区打完标记幸存后放入S0区,然后需要将E区和S1区删除,然后等下一次E区快满的时候将S0区、E区所有对象进行打标全部复制到S1区。S0和S1交替使用作为幸存下来的区域。

即E+S1复制到S0、E+S0复制到S1、E+S1复制到S0。。。。。反复执行。

比复制算法直接1分为2内存的利用率高点。

然后再看old区:

其实每次Young GC这个对象的年龄就会+1,即如果在这次GC后这个对象活下来了其年龄就+1.

如果age=15,就不再往S区复制了而是直接到Old区。

Old区除了存了年龄》=15岁的对象还存储“大对象”(大对象的复制消耗比较大,所以直接存到old区)

如果Old区快满的时候也会进行GC,Old GC一般会伴随Young GC,所以Old GC又叫Full GC,会引起整个java程序暂停然后全力的进行垃圾回收,通过标记清理或标记整理的算法。

总结:标记清理或标记整理主要用于Full GC,复制算法主要用于Young GC.

年轻代中比较有名的收集器是ParNew,老年代中比较有名的垃圾收集器是CMS。

最新版的JDK不再建议用以前的垃圾收集器了,而是采用全新的G1垃圾收集器