弄清Java虚拟机GC的运行过程

前言:要弄清Java虚拟机GC的整个过程,就得弄明白Java虚拟机用什么来进行GC?Java虚拟机在哪里GC?何时GC?GC什么?算法

开门见山

GC(Garbage Collection)垃圾收集,JVM一个很是重要的功能。本文将围绕着JVM的GC这个动做展开,来过一遍GC的整个运做过程。post

JVM用什么来进行GC

JVM是GC的发起者,准确说是VMThread是GC的发起者,那用什么来进行GC呢?很显然,用到的是垃圾收集器来进行GC的。而垃圾收集器,能够根据堆中的分代,分为不一样类型的垃圾收集器。this

新生代(Young generation)spa

  • Serial垃圾收集器
  • ParNew垃圾收集器
  • Parallel Scavenge垃圾收集器

老年代(Tenured generation)线程

  • CMS垃圾收集器
  • Parallel Old垃圾收集器
  • Serial Old垃圾收集器

G1是一个特殊的垃圾收集器,既能够做为新生代的垃圾收集器,也能够做为老年代的垃圾收集器。3d

image

更加详细的垃圾收集器的知识,能够阅读《深刻理解JVM》这本书。cdn

JVM在哪里进行GC

JVM的GC动做只在两个地方回收————堆和方法区。(JDK8的metaspace的GC,这里暂不讨论。若是还有其余内存区域发生垃圾回收,请指正)对象

在堆取进行GCblog

首先,得了解堆是什么,有什么做用?言简意赅——总结Java内存区域和常量池队列

这里须要知道,JVM的GC采用了分代思想,因此堆被分红了新生代和老年代,而新生代又被细分为Eden区和From survivor和To survivor区。当类被JVM加载后,Java应用程序运行而后某个对象被new,这个对象就会被分配到堆中的新生代的Eden区(对象优先被分配到Eden区),若是Eden区域没有足够的内存来分配给该对象,就会触发minor GC来清除已经“死亡”的对象,这样才能将新清出的内存分配给该对象,通过GC都还存活的对象,会被移至From survivor区中。因为新生代采用的复制算法,Eden区存活对象和From survivor区的存活对象将被复制到To survivor区中。在To survivor区中的对象,每通过一次GC,对象中的“年龄计数器”就会加1,若是超过了晋升为老年代的年龄阈值时(默认为15)对象就会晋升到老年代中。

因为老年代里存放的都是大对象、存活时间较久的对象,所以老年代通常都是用标记-整理算法或标记-清除算法。当老年代对象无法再分配内存时,会触发一次Major GC(Full GC),用于回收那些已经“死亡”的对象。

下图为堆中分代

image

在方法区中进行GC

在堆中进行GC通常能够回收70%~95%的空间,相比在方法区中进行GC效率是很是低的。可是效率低不表明不进行GC。永久代的垃圾收集主要回收两部份内容:废弃常量和无用的类。

JVM何时进行GC

在上文中,已经讲到了JVM何时进行GC。就是在Eden区没有足够内存分配给对象的时候进行Minor GC,在老年代无法再分配内存给大对象以及“老”对象的时候进行的Major GC(full GC)。这里谈一谈Minor GC和Major GC:

  • 新生代GC(Minor GC):指发生在新生代的垃圾收集动做,由于Java对象大多都具有招生夕灭的特性,因此Minor GC很是频繁,通常回收速度也很是快。
  • 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,常常会伴随至少一次的Minor GC(并非绝对的,例如Parallel Scavenge垃圾收集器就直接进行Major GC的策略选择过程)。Major GC的速度通常比Minor GC慢10倍以上。

JVM对什么对象进行GC

这里,首先得了解对象在什么状况下会被进行GC?

是“真正死亡”的对象吗?

那么,对象怎么才能被断定为“真正死亡”呢?JVM是经过可达性分析来进行分析的。可达性分析就是经过从GC Roots为起点,判断是否有一个引用链与被判断的对象相连,若是相连这表明这个对象还“活着”,是可用的。然而,一个对象被真正断定为“死亡”,须要进行两次标记(被标记为可回收对象),而后在下一次JVM进行GC的时候,才被真正的回收掉。若是一个对象没有与GC Roots的引用链相链接,也并无被真正断定为“死亡”,而是进行第一次标记,而后在第二次标记以前会进行“自救”过程。所谓的“自救”就是在第二次被标记以前,须要从新与引用链相链接。在第一次被标记后,对象会进行筛选,筛选的条件为是否有必要进行执行finalize()方法。若是没有必要执行finalize()方法,则就等待第二次被标记;若是有必要执行finalize()方法,则会先将对象放置在一个叫F-Queue的队列中,而后等待虚拟机经过Finalizer线程去触发对象的finalize()方法,而后在finalize()方法里面,对象就开始自救的过程。对象自救能够经过把this赋值给某个类变量或者对象的成员变量,就实现了和引用链从新链接的目的——自救成功。所以在第二次标记的时候就会把对象从F-Queue队列中移除,而后虚拟机会在F-Queue中进行第二次小规模的标记,而后就等待下一次GC的来临。

结论:

因此,从上面分析的结果来看,JVM进行GC的对象,就是没有和引用链上相连的而且通过第一次标记,没有“自救”成功的对象。

相关文章
相关标签/搜索