你应该知道的Java垃圾收集器 - 串行、并行、CMS、G1

当我们谈论垃圾收集时,绝大多数人都知道这个概念,并在日常编程中使用它。即使如此,有关垃圾收集,我们很多人还是不太明白。关于JVM的一个最大的误解是它有一个垃圾收集器,其实它提供了四个不同的垃圾收集器,每一个都有自己独特的优点和缺点。重要的是,我们编程的时候可以通过JVM选择垃圾回收器类型。我们通过向JVM传递参数进行选择。每种类型在很大程度上有所不同并且可以为我们提供完全不同的应用程序性能。理解每种类型的垃圾回收器并且根据应用程序选择进行正确的选择是非常重要的。

这四种垃圾收集算法的共同点是,它们都是分代的,这意味着它们将堆分成不同的段,都使用由来已久的规则,就是堆中的大多数对象都是短暂(short lived )的,应该被快速回收。关于这方面的内容,我们以后会专门讨论。

1.串行收集器(Serial Collector)

串行收集器是最简单的,并且您可能不会使用它,因为它主要设计用于单线程环境(例如32位或Windows)和以及用于比较小的堆。此收集器会在自己工作的时候冻结所有应用程序线程,所以可能不适合服务器环境。它最适合的是简单的命令行程序。

通过JVM参数-XX:+UseSerialGC可以使用串行垃圾回收器。

2.并行/吞吐量收集器(Parallel / Throughput collector)

接下来是并行收集器,这是JVM的默认收集器。很像它的名字,它的最大的优点是使用多线程来扫描和压缩堆。并行收集器同样有个缺点就是在它执行 minor或者 full 垃圾回收时将会停止所有的应用程序线程。并行收集器最适合应用程序,可以容忍应用程序的暂停,并试图优化来降低收集器导致的CPU开销。

3. CMS收集器(CMS Collector)

接下来是并行收集器是CMS收集器(“并发标记扫描”)。此算法使用多个线程(“并发”)扫描整个堆(“mark”),以查找可以回收的未使用对象(“sweep”)。该算法将在两种情况下进入“stop the world”(STW)(文末有解释)模式:当初始化根(可以从线程入口点或静态变量访问的旧代中的对象)的初始标记时,以及当应用改变堆的状态的时候该收集器同时并发运行的时候,会强制cms再重新回去做一次执行来确保cms有正确的对象被标记。

使用此收集器时最大的问题是遇到“推荐失败(promotion failures)”,就是在收集年轻和老年代之间发生竞争状况(race condition)的情况。如果收集器需要将young objects推荐到old generation,但没有足够的时间来清除它,这时候将导致一个完整的STW收集 - 这是CMS收集器要特别要注意避免的。为了确保这不会发生,你可以将old generation的大小增加一点(或把整个堆的大小变大一点)或给收集器分配更多的后台线程,以便能超过“对象分配的速度“。

与并行收集器相比,该算法的另一个缺点是,它会使用更多的CPU,它通过使用多个线程执行扫描和收集,为应用程序提供更高水平的连续吞吐量。对于大多数长期运行的服务器应用程序,这种收集器不会让应用程序冻结,是一个合适的选择。即便如此,此算法默认情况下不启用。您必须指定XX:+ USeParNewGC才能实际启用它。如果你愿意分配更多的CPU资源,以避免应用程序暂停,并且你的堆小于4Gb的大小,就是适合使用此收集器。然而,如果堆大于4GB,那么你可能更适合最后一个算法--G1收集器。

4. G1收集器

在JDK 7 update 4 中引入的“第一收集器”(G1),是专门为更好地支持大于4GB的堆而设计的。 G1收集器利用多个后台线程来扫描堆,将其划分为多个区域,范围从1MB到32MB(取决于堆的大小)。 G1收集器首先会去扫描那些包含最多垃圾对象的区域,这种做法我们起名叫:Garbage first。此收集器使用-XX:+ UseG1GC标志打开。

这个收集器会出现STW的情况,就是在后台线程完成扫描未使用的对象之前堆被如果被耗尽的话,在这种情况下,收集器将不得不停止应用程序然后进入STW收集的状态。 G1还有另一个优点,就是它在执行的过程中可以顺便对堆进行压缩,这个能力CMS收集器只能在full STW收集期间才能做的。

最近的几年,大堆成了一个比较有争议的领域。越来越多的开发人员开始从单体化应用的那种一个机器一个JVM转移去开发更多的微服务,更多的基于模块的架构,这种情况下,每台机器上就会有多个JVM。

这是由许多因素驱动的,包括隔离应用程序的不同的部分,简化部署以及降低将应用程序的类们reloading到内存中的成本等等因素(在Java 8中实际已经又了许多改进)。即使如此,对JVM而言,最大的驱动因素之一就是希望避免那些长时间STW停顿(在对较大的堆中进行收集的时候,需要花很长时间)。 Docker等容器技术也加速了这一点,使您能够在相同的物理机器上轻松部署多个应用程序。

Java 8和G1收集器

另一个不错的改进就是Java 8 update 20的时候G1收集器开始支持字符串重复数据删除(String deduplication)。因为字符串(它们的内部是char []数组)占用了我们的大部分堆,所以进行了一个新的优化,使得G1收集器能够识别在整个堆中多次重复的字符串,并将它们纠正为指向同一个内部char[] 数组,以避免相同字符串的多个副本在堆内无效率地驻留。您可以使用-XX:+ UseStringDeduplicationJVM参数来尝试此操作。

Java 8和PermGen

Java 8中最大的变化之一是删除了传统上为类元数据、内部字符串和静态变量分配的堆的permgen部分。在这之前,一般都要求开发人员来优化和tuning。这也是多年来成为许多OutOfMemory异常的源头,所以这个优化对我们帮助很大。即使如此,这也不会减少开发人员将他们的应用程序解耦到多个JVM的微服务的浪潮。

每个收集器都设置了不同的一个切换和开关,每个都有自己的特点,选择哪一款还是取决于你具体的应用程序的具体要求。

Garbage Collection JVM参数

Option

Description

-XX:+UseSerialGC

Serial Garbage Collector

-XX:+UseParallelGC

Parallel Garbage Collector

-XX:+UseConcMarkSweepGC

CMS Garbage Collector

-XX:ParallelCMSThreads=

CMS Collector – number of threads to use

-XX:+UseG1GC

G1 Gargbage Collector

GC优化参数:

Option

Description

-Xms

Initial heap memory size

-Xmx

Maximum heap memory size

-Xmn

Size of Young Generation

-XX:PermSize

Initial Permanent Generation size

-XX:MaxPermSize

Maximum Permanent Generation size

JVM GC参数的使用示例:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m

小卡片:什么是STW"stop-the-world" 机制简称STW,即,在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起Java中一种全局暂停的现象全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互多半由于GC引起Dump线程死锁检查堆DumpGC时为什么会有全局停顿?–类比在聚会时打扫房间,聚会时很乱,又有新的垃圾产生,房间永远打扫不干净,只有让大家停止活动了,才能将房间打扫干净。危害长时间服务停止,没有响应;遇到HA系统,可能引起主备切换,严重危害生产环境。

  • Java中一种全局暂停的现象
  • 全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互
  • 多半由于GC引起
  • Dump线程
  • 死锁检查
  • 堆Dump
  • GC时为什么会有全局停顿?

–类比在聚会时打扫房间,聚会时很乱,又有新的垃圾产生,房间永远打扫不干净,只有让大家停止活动了,才能将房间打扫干净。

  • 危害
  • 长时间服务停止,没有响应;
  • 遇到HA系统,可能引起主备切换,严重危害生产环境。

原文发布于微信公众号 - ImportSource(importsource)

原文发表时间:2016-11-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JavaEdge

JDK7新特性概览JSR292:支持动态类型语言(InvokeDynamic)G1 垃圾回收器(Garbage-First Collector)JSR334:小的语言改进(Project Coin)核

796100
来自专栏杨建荣的学习笔记

system表空间不足的问题分析(r6笔记第66天)

很多事情见多了也就有了麻木的感觉,报警短信就是如此,每天总能收到不少的报警短信,可能很多时候就扫一眼,如果没有严重的问题自己是不会情愿打开电脑处理的。 对于此,...

28840
来自专栏张善友的专栏

跨平台的 .NET 运行环境 Mono 3.2 新特性

Mono 3.2 发布了,对 Mono 3.0 和 2.10 版本的支持不再继续,而且这两个分支也不再提供 bug 修复更新。 Mono 3.2 主要新特性: ...

20580
来自专栏hbbliyong

JAVA试练塔之试炼技能图

1.计算机基础: 1.1数据机构基础: 主要学习: 1.向量,链表,栈,队列和堆,词典。熟悉 2.树,二叉搜索树。熟悉 3.图,有向图,无向图,基本概念 4.二...

40370
来自专栏我是攻城师

Java垃圾收集器之G1介绍(一)

34650
来自专栏美团技术团队

从实际案例聊聊Java应用的GC优化

当Java程序性能达不到既定目标,且其他优化手段都已经穷尽时,通常需要调整垃圾回收器来进一步提高性能,称为GC优化。但GC算法复杂,影响GC性能的参数众多,且参...

634120
来自专栏Spark学习技巧

从零讲Java,给你一条清晰地学习道路!该学什么就学什么!

12950
来自专栏不会写文章的程序员不是好厨师

一次频繁cms gc问题的排查

前几天收到线上某机器cms-initial-remark次数超过13次报警,这台机器长时间没有过新功能上线,为啥会出现频繁cms gc告警呢,遂一起排查。

17730
来自专栏IMWeb前端团队

使用localStorage必须了解的点

本地存储(localStorage)已经不是新鲜的概念,本文并不是本地存储的概念及 API 介绍,而是对本地存储在实际业务场景中的一些问题做些探讨,从而形成一套...

259100
来自专栏涂小刚的专栏

从 PageRank Example 谈 Spark 应用程序调优

在做PageRank测试时,发现有很多有趣的调优点,想到这些调优点可能对用户来说是普遍有效的,现把它整理出来一一分析,以供大家参考。

1.4K30

扫码关注云+社区

领取腾讯云代金券