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

栏目: Java · 发布时间: 6年前

内容简介:前言:要弄清Java虚拟机GC的整个过程,就得弄明白Java虚拟机用什么来进行GC?Java虚拟机在哪里GC?什么时候GC?GC什么?GC(Garbage Collection)垃圾收集,JVM一个非常重要的功能。本文将围绕着JVM的GC这个动作展开,来过一遍GC的整个运作过程。JVM是GC的发起者,准确说是VMThread是GC的发起者,那用什么来进行GC呢?很显然,用到的是垃圾收集器来进行GC的。而垃圾收集器,可以根据堆中的分代,分为不同类型的垃圾收集器。

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

开门见山

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

JVM用什么来进行GC

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

新生代(Young generation)

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

老年代(Tenured generation)

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

G1是一个特殊的垃圾收集器,既可以作为新生代的垃圾收集器,也可以作为老年代的垃圾收集器。

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

更加详细的垃圾收集器的知识,可以阅读《深入理解JVM》这本书。

JVM在哪里进行GC

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

在堆取进行GC

首先,得了解堆是什么,有什么作用? 言简意赅——总结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),用于回收那些已经“死亡”的对象。

下图为堆中分代

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

在方法区中进行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的对象,就是没有和引用链上相连的并且经过第一次标记,没有“自救”成功的对象。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ruby元编程

Ruby元编程

[意] Paolo Perrotta / 廖志刚、陈睿杰 / 华中科技大学出版社 / 2012-1-10 / 56.00元

《Ruby元编程》以案例形式循序渐进讲解Ruby对象模型原理和高级应用技巧,堪称动态语言的设计模式。书中讲述的各种Ruby编程模式,完全可以应用于其他动态语言(甚至静态语言)。本书不仅适合Ruby程序员阅读,也适合对动态编程 语言和面向对象编程感兴趣的读者阅读。所有对程序设计理论感兴趣的人都能从中获益。Ruby之父松本行弘作序推荐。一起来看看 《Ruby元编程》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具