Java垃圾回收精粹分4个部分,本篇是第1部分。在第1部分里介绍了权衡点、对象生命周期以及全局暂停事件。

串行、并行、并发、CMS、G1、年轻代(Young Gen)、新生代(New Gen)、旧生代(Old Gen)、持久代(Perm Gen)、伊甸区(Eden)、年老区(Tenured)、幸存区(Survivor Spaces)、安全点(Safepoints)以及数百种JVM启动参数。当你试图调整垃圾回收器使你的Java应用程序获得需要的吞吐量和延迟时,这些会难倒你吗?如果回答是,你也不必担心,这样的人还有很多。文档中对垃圾收集的描述感觉就像飞机操作手册一样复杂,每个旋钮和转盘足够详尽细致,但就是搞不清如何飞起来。本文将试图解释在为特定工作负载选择和调整垃圾收集算法时需要了解的权衡点。

本文将重点关注甲骨文的HotSpot JVM和OpenJDK,这俩是最常见的。最后也会讨论其他商业JVM作为替代方案。

权衡点(The Tradeoffs)

民间有句俗话叫:“舍不得孩子套不着狼”。若我们想得到些东西,我们通常必须放弃另一些东西。当这句话用在垃圾回收上,主要通过三个变量为GC设定目标:

  1. 吞吐量:花费在垃圾收集上的时间占整个应用程序工作量的比例。通过“-XX: GCTimeRatio = 99;”来设置目标吞吐量;默认值是99,这意味着有1%的时间用于垃圾收集。
  2. 延迟:由于垃圾回收引起的响应暂停的时间。设置GC暂停的延迟使用“- XX :MaxGCPauseMillis =< n>”。
  3. 内存:我们的系统内存通常用来存储状态,在管理时经常要进行复制和移动。在任何时间点,被应用程序保留的活动对象集被称为存活集。“- Xmx<n>”用来设置最大堆大小,可以用来调节应用程序可用的堆大小。

注意:通常HotSpot不能达到这些指标,而且会没有任何警告默默地继续运行,与设定的参数值愈行愈远。

延迟散布在整个垃圾回收过程中。它会以稍稍加一些平均延迟时间为代价,减少在最坏情况下的延迟或者使延迟不会过于频繁地出现,让我们可以接受。我们不要把术语“实时”理解为尽可能小的延迟,而应当看做无论吞吐量如何都保证有一个明确的短暂延迟。

当然,对于有些程序的工作负载吞吐量是最重要的。譬如,一个长时间运行的批处理作业;如果一个批处理作业为了垃圾收集而偶尔停顿几秒,只要整体上可以更快完成那就没问题。

实际上所有其他工作负载,从人机交互的应用程序到金融交易系统,如果系统出现没有响应超过几秒钟,在某些情况下甚至是几毫秒,那麻烦就大了。在金融交易系统中牺牲吞吐量换延迟常常是值得的。当然也有可能是,应用程序被可用的物理内存数限制住却又必须保持运行,在这种情况下,我们不得不放弃对延迟和吞吐量方面的性能追求。

权衡结果通常是这样的:

  • 在很大程度上,可以使用内存消耗更多的垃圾收集算法减少垃圾收集的平均占用时间。
  • 可预见的最坏情况是延迟引发的停止响应。可以通过限制活动集(live set)和维持较小的堆来减少这种情况。
  • 可以通过管理堆和代大小,以及控制应用程序的对象分配率来减少暂停发生的频率。
  • 较长的暂停的发生频率可以通过并行运行GC和应用程序来减少,但有时会影响吞吐量。

对象生命周期

垃圾收集算法的优化通常假设大多数对象都存活了一个很短的周期,只有少部分存活时间很长。在大多数应用程序里,多数对象有一个明确的生命周期,仅有很小的一部分对象会贯穿运行整个过程。在垃圾收集理论中,这种现象通常被称为“infant mortality”(婴儿死亡率) 或 “weak generational hypothesis”(弱代假设)。譬如,循环迭代器大多生命周期极短,而静态字符串则在整个程序生命周期中都有效。

实验表明,分代垃圾收集器常常比不分代有一个数量级的提升,因而在几乎所有的服务器JVM中都使用分代垃圾回收。通过为对象分代我们知道,一块新分配对象的区域里存活对象往往非常少。因此,使用一个收集器清理这些新生的少数存活对象,并将它们拷贝到另一个年老代的区域里会很奏效。Hotspot垃圾回收器使用在GC周期中幸存的次数作为一个对象的年纪。

注意:如果你的应用程序不断产生大量对象并且存活很久,显而易见你的应用程序将要花上不少时间在垃圾回收上。同样昭然若揭的是,你会花不少时间来调优Hotspot的垃圾回收器。这种情况下,代“过滤器”不太有效。它会频繁地收集长时生存代而花费了颇多时间,最终导致了GC效率低下。年老代又那么分散,所以年老代的收集算法效率更低。分代垃圾收集器往往在两个不同回收周期操作:收集完短时间存活对象之后的次要回收(Minor collections);以及较低频率的主要回收(Major collections),即在对旧生代区域进行回收之后。

全局暂停事件(stop-the-world events)

在垃圾回收过程中的程序暂停被称之为“全局暂停事件”。在实际工程中为了进行内存管理,垃圾回收器必须定期这样做。根据不同的算法,不同的收集器会在不同的时间、不同的地方暂停整个程序。为了完全停止程序,你必须暂停所有运行中的线程。“安全点”是指在程序执行中到达的某个点,此时GC根对象全都是已知的,并且所有的堆对象的内容是一致的。当运行到 “安全点”的时候,垃圾回收器会发送一个信号让线程暂停。根据线程正在做的事情,可能需要花费一些时间才能到达“安全点”。“安全点”的检查通常是在函数的返回或者循环边界结束,但是可以进行优化使其更加动态。比如:某线程正在复制一个大数组,克隆一个大的对象,或者执行一个有限次的计数循环,这可能需要几毫秒才能到达一个“安全点”。 “安全点抵达时间”(TTSP)对于低延迟的应用非常重要。除了其他的GC标志之外,启用“XX:+PrintGCApplicationStoppedTime ”标志可以输出这个时间。

注意:对于有大量并行线程的应用程序来说,当全局暂停发生时,系统将会发生明显的调度压力并在结束后恢复。因此依赖全局暂停(stop-the-world)较少的算法会更有效率。

Java垃圾回收精粹 — Part1的更多相关文章

  1. Java垃圾回收精粹 — Part4

    Java垃圾回收精粹分4个部分,本篇是第4部分.在第4部分里介绍了G1收集器.其他并发收集器以及垃圾收集监控和调优. Garbage First (G1) 收集器 G1 (-XX:+UseG1GC)收 ...

  2. Java垃圾回收精粹 — Part3

    Java垃圾回收精粹分4个部分,本篇是第3部分.在第3部分里介绍了串行收集器.并行收集器以及并发标记清理收集器(CMS). 串行收集器(Serial Collector) 串行收集器是最简单的收集器, ...

  3. Java垃圾回收精粹 — Part2

    Java垃圾回收精粹分4个部分,本篇是第2部分.在第2部分里介绍了Hotspot中的堆结构.对象分配以及次要回收. Hotspot中的堆结构 理解不同的收集器的工作方式,是探讨Java堆结构如何支持分 ...

  4. 【转载】Java垃圾回收机制

    原文地址:http://www.importnew.com/19085.html Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联 ...

  5. 【转】深入理解 Java 垃圾回收机制

    深入理解 Java 垃圾回收机制   一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  6. 深入理解java垃圾回收机制

    深入理解java垃圾回收机制---- 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  7. Java GC系列(2):Java垃圾回收是如何工作的?

    本文由 ImportNew - 伍翀 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 本教程是为了理解基本的Java垃圾回收以及它是如何 ...

  8. Java GC系列(1):Java垃圾回收简介

    本文由 ImportNew - 好好先生 翻译自 javapapers. Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Ja ...

  9. Java垃圾回收介绍(译)

    在Java中,对象内存空间的分配与回收是由JVM中的垃圾回收进程自动完成的.与C语言不同的是,在Java中开发者不需要专门为垃圾回收写代码.这是使Java流行的众多特征之一,也帮助了程序员写出了更好的 ...

随机推荐

  1. IE7、IE8下使用escape、encodeURI传递中文参数乱码的问题及解决方案

    js跳转到指定页面,一旦escape()中文数据,浏览器就会终止和没有反应.上网搜了半天始终不得解.一种说法是,escape中文之后,url中出现了%u,IE7和IE8拒绝执行.目前看来差不多是这样的 ...

  2. AC日记——松江1843路 洛谷七月月赛

    松江1843路 思路: 三分: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define ...

  3. 第三方登陆微博、qq、微信

    源文:http://blog.csdn.net/tivonalh/article/details/60954373 假设是已经申请完成各平台开发者账号. 先来简单的,微博和QQ 微博: 引入微博JS ...

  4. 免费的.NET混淆和反编译工具

    免费的.NET代码混淆工具: Eazfuscator.NET  http://www.foss.kharkov.ua/g1/projects/eazfuscator/dotnet/Default.as ...

  5. pyqt5简单登陆界面

          登陆界面姓名输入错误会弹出错误信息.正确就会弹出第二个窗体. # -*- coding:utf-8 -*- import sys from PyQt5.QtWidgets import Q ...

  6. bzoj 1818 主席树

    思路:主席树搞一搞. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #de ...

  7. Docker下使用daocloud镜像加速(基于Centos6)

    Docker加速器使用时不需要任何额外操作.就像这样下载官方Ubuntu镜像 实际操作(添加镜像源):在 /etc/sysconfig/docker下添加两条命令 other_args="- ...

  8. hihocoder 1500 EL SUENO

    树上背包. 简单的树形$dp$,计算出摧毁每一个节点所需的最小费用,背包即可. #include<bits/stdc++.h> using namespace std; struct X ...

  9. Android之 内容提供器(1)——使用内容提供器访问其它程序共享的数据

    (下面内容是阅读郭霖大神的<第一行代码>总结的) 1 概述 内容提供器是Android实现跨程序共享数据的标准方式. 内容提供器的的使用方法有两种, 一是使用已有的内容提供器对其他程序的数 ...

  10. hdu 4276 树形dp

    题意:给你n个点,n-1条边构成树,每条边有边权(表示走每条边的时间),每个点有点权,问在时间T从点1走到点n,能够得到最多的点权有多少. 题目链接:点我 由于是树,最优的结果一定经过最短路,其他边要 ...