美国上市公司,专注Java培训22年

Java学习笔记之垃圾回收机制


通常来说,要写Java代码,你基本上都没必要听说垃圾回收这个概念的。这不,对于已经写了5年多Java代码的我来说,我还没有哪次经历说是需要使用垃圾回收方面的知识来解决问题的。但是,我依然督促自己花了几天时间系统性地(也比较浅显地)学习了Java垃圾回收机制。我认为学习Java垃圾回收机制至少可以得到以下几方面的好处:

对于系统调优有直接帮助

增加和同行聊天或者下一份工作面试时的谈资

在追求技术卓越上更进一步

(一)Java堆内存的分代管理

Java垃圾回收是需要消耗CPU和内存资源的,其速度随着内存的变大而减慢,这将严重影响系统的性能。同时,Java系统中存在着这么一种现象:大多数Java对象都是“短命”的。基于此,Java采用了分代的内存管理方式,并在不同的内存代中采用不同的垃圾回收算法,从而达到对内存更细粒度的管理,最大限度地减小垃圾回收对系统本身的影响。

【Java学习笔记之垃圾回收】

由上图所示,Java的堆空间被分为了三个区域,分别是新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。新创建出来的对象首先存放在新生代,经过新生代中多次垃圾回收(在Survivor 0和Survivor 1之间来回复制),存活下来的对象将被转移到老年代。新生代中垃圾回收很频繁,这样多数“短命”的对象将得到及时清理;又由于新生代内存空间通常不大,回收速度也相对较快。在老年代中,存放着从新生代中经历了多次垃圾回收后仍然存活的对象,这些对象相对较少,而老年代内存一般很大,并不容易塞满,因此老年代的垃圾回收频率要远远低于新生代,从而减少了对系统性能的影响。永久代中主要存放Java类本身的数据信息,当Java类不再被使用时,也会被垃圾回收掉。开发者们通常无法预测永久代的大小,导致程序经常出现 “java.lang.OutOfMemoryError: Permgen space”错误,因此在Java 8中,使用jvm进程原生内存空间的Metaspace代替了永久代。在默认情况下,Metaspace将使用jvm进程所有可用的内存。

在新生代进行的GC叫做minor GC,在老年代进行的GC都叫major GC,Full GC同时作用于新生代和老年代。在垃圾回收过程中经常涉及到对对象的挪动(比如上文提到的对象在Survivor 0和Survivor 1之间的复制),进而导致需要对对象引用进行更新。为了保证引用更新的正确性,Java将暂停所有其他的线程,这种情况被称为“Stop-The-World”,导致系统全局停顿。Stop-The-World对系统性能存在影响,因此垃圾回收的一个原则是尽量减少“Stop-The-World”的时间。

【Java学习笔记之垃圾回收】

上图展示了不同垃圾收集器的Stop-The-World情况,可以看出Serial、Parallel和CMS收集器均存在不同程度的Stop-The-Word情况;而即便是最新的G1收集器也不例外。

(二)垃圾回收算法

最早的垃圾回收算法有引用计数法,但由于其性能不好以及无法回收循环引用对象的问题,工程上并没有得到使用。当前Java的垃圾回收主要基于标记-清除(Mark-Sweep)算法,该算法大致包括两个步骤:

从GC ROOT对象开始标记所有可达对象,GC ROOT包括局部变量、静态变量及运行中的线程对象等。

清除掉未被标记的对象

标记-清除算法是Java垃圾回收的基本原则,在此基础上,Java还提供了几种变种算法,包括标记-压缩(Mark-Sweep-Compact)算法和标记-复制(Mark-Copy)等。

标记清除算法(Mark Sweep)

标记清除算法的原理即上文中提到的两个步骤,这种算法的优点是可以减少Stop-The-World的时间,缺点是会造成内存碎片,如下图所示:

【Java学习笔记之垃圾回收】

标记压缩算法(Mark Sweep Compact)

为了解决内存碎片问题,标记压缩算法(如下图所示)在回收内存之后会将存活的对象集体压到内存的一端。压缩过程需要更新对象的引用,如前文所述,这将增加系统Stop-The-World时间。

【Java学习笔记之垃圾回收】

标记复制算法(Mark Copy)

标记复制算法是一种效率相对较高的算法,因为它不涉及对无用对象的删除,只需要将标记存活的对象从一个内存区拷贝到另一个内存区。但是标记复制算法不适用于存活对象较多的老年代,因为大量的对象拷贝会降低系统性能。Java在新生代中主要采用了标记复制算法,其中包括从Eden区到Survivor区的复制和两个Survivor区之间的复制。

【Java学习笔记之垃圾回收】

(三)垃圾收集器

在Java中主要有4中垃圾收集器,他们各自对于不同的内存代采用不同的算法。Java会根据当前系统的基本配置确定一个默认的垃圾收集器,你可以通过以下命令查看:

java -XX:+PrintCommandLineFlags -version

在笔者的电脑上输出为:

-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseParallelGC java version "1.8.0_45" Java(TM) SE Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

由红色部分可以看出,默认情况下使用了Parallel收集器,这也是多数Java机器(特别是服务器)默认的垃圾收集器。

串行收集器(Serial Collector)

顾名思义,串行收集器指采用单线程进行垃圾回收,回收时会导致长时间的Stop-The-World,主要用于单机程序。该收集器在新生代采用复制算法,在老年代采用标记-压缩算法。可以通过-XX:+UseSerialGC命令行选项激活该收集器。

并行收集器(Parallel Collector)

该收集器同样在新生代采用复制算法,在老年代采用标记-压缩算法,只是使用了多线程的方式进行垃圾回收,从而大大提高了回收效率,但是回收过程中同时需要Stop-The-World。可以通过-XX:+UseParallelGC激活该收集器。多数情况下,并行收集器是Java的默认收集器。

并发标记清除收集器(Concurrent Mark Sweep Collector,CMS)

该收集器在在新生代中采用复制算法,在老年代采用标记-清除算法(不是标记-压缩)。之所以叫“并发”,是因为在回收过程的某些阶段,回收线程和用户线程同时执行,当然不是整个回收过程都可以和用户线程并行的,该收集器也存在Stop-The-World的时候,只是相比于其他收集器来说Stop-The-World持续时间较少而已。可以通过-XX:+UseConcMarkSweepGC激活该收集器。

G1收集器(Garbage First Collector)

G1收集器是Java世界最新的收集器,在Java 9中,它将成为默认的垃圾收集器。该收集器采用与上文中提到的收集器不同方式来对待Java对内存,如下图所示。可以通过-XX:+UseG1GC激活该收集器。

【Java学习笔记之垃圾回收】

【免责声明】本文部分系转载,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,如涉及作品内容、版权和其它问题,请在30日内与我们联系,我们会予以重改或删除相关文章,以保证您的权益!

Java开发高端课程免费试学

大咖讲师+项目实战全面提升你的职场竞争力

  • 海量实战教程
  • 1V1答疑解惑
  • 行业动态分析
  • 大神学习路径图

相关推荐

更多
  • 一个故事讲完https
    一个故事讲完https
    感谢大家阅读由java培训机构分享的“一个故事讲完https”希望对大家有所帮助 详情>>

    2017-08-31

  • GitHub 上火的 Java 框架
    GitHub 上火的 Java 框架
    Java 是目前最需要的编程语言之一。在这里,我们已经挖掘了一些关于框架趋势的有用信息,也就是最受开发者青睐的 Java 框架,名单如下 详情>>

    2017-10-24

  • Java入门学习路径,没有之一
    Java入门学习路径,没有之一
    作为刚刚进入Java领域的新同学,无论是高校的毕业大学生,还是有志转行的在职人员,都面临着诸多的困惑。今天java培训班就来为大家讲解下Java入门学习路径 详情>>

    2017-11-23

  • java初学者学习心得
    java初学者学习心得
    学习了一学期的Java课程,觉得是该总结自己的心得体会了。开始学习任何一门课(包括java),兴趣最重要。下面请看java培训机构带来的分享 详情>>

    2017-12-12

  • Java开班时间

    收起