Java垃圾回收精粹分4个部分,本篇是第2部分。在第2部分里介绍了Hotspot中的堆结构、对象分配以及次要回收。

Hotspot中的堆结构

理解不同的收集器的工作方式,是探讨Java堆结构如何支持分代机制的最好的方式。

伊甸区(Eden)的大部分对象都是刚刚被分配的。幸存区(survivor)用来临时存储那些从伊甸区里幸存下来的对象。当我们讨论完次要回收(minor collections)后将描述幸存区的用途。伊甸区和幸存区统称为“年轻代”或 “新生代”存活足够久的对象,将最终移到年老(tenured )区里。

永久代在运行时存储永不销毁对象的区域,比如像类和静态字符串。不幸的是,在许多应用程序中,持续运行的类加载这一常见用途背后隐藏了一个激进的假设:即类是不会被销毁。在Java 7中,本地化的String会从永久代(perm gen)代移动到年老区。从Java 8开始,HotSpot虚拟机中删除了“永久代”。这是题外话,不再过多探讨。大部分其他的商业收集器不使用一个单独的永久代,而是往往把所有长期存活的对象放到年老代里面。

注意:虚拟空间允许收集器调整区块大小,以满足延迟和吞吐量的要求。收集器对每一次的收集做统计,并调整相应区的大小以达设定的目标。

对象分配

为了避免竞争,每个线程都指派了一个“线程本地分配缓冲区”(Thread Local Allocation Buffer,缩写TLAB)用于分配对象。TLAB通过避开单个内存资源竞争问题的方式,使得对象分配的规模可以等同于线程的数量。用TLAB分配对象是一个廉价的操作,只是为对象的大小分配一个指针,在大部分平台上需要约10个指令。Java堆内存的分配比C运行时使用malloc ()分配内存更加廉价。

注意:有鉴于个别对象分配是很廉价的,次要回收(minor collection)发生率必须与对象分配的速率成正比。

当一个TLAB被耗尽后,线程可以简单地从伊甸区请求一个新的。当伊甸区用完后,开始一次次要回收。

大对象(-XX:PretenureSizeThreshold=<n>)在年轻代的分配可能失败,因此必须分配在老年代,比如:大数组。如果Threshold(阈值)的设置低于TLAB大小,那么遇到合适TLAB的对象就不会创建在年老代。G1的新收集器在处理大对象的时候有所不同,将在后面部分单独讨论。

次要回收(Minor Collections )

当伊甸区填满时会触发次要回收。这一过程将所有新生代里存活的对象适当的复制到幸存区(survivor space)和年老区(tenured space)。复制到年老区通常称为晋升(promotion)或者老年化(tenuring)。晋升发生在那些对象足够老(– XX:MaxTenuringThreshold=<n>)或者幸存空间溢出时。

存活对象是指那些应用程序可以访问到的对象;任何不能访问的其他对象,都可以认为是死对象。在次要回收中,存活对象的复制从GC根开始,逐个复制这些可访问的对象到幸存区最后完成。

GC根通常包括应用程序的引用、JVM内部的静态字段的引用以及线程堆栈帧的引用,所有这些构成了应用程序的可访问对象图。

在分代回收中,新生代可访问对象图中的GC根还包括年老代对新生代的任何引用。这些引用也必须进行处理,以确保在新生代里面所有可访问对象在次要回收后仍然是存活的。识别这些跨代引用使用了“卡片表(card table)”。Hotspot 的卡片表是一个bytes数组,其中每个字节用于跟踪相对应年老代里的512byte区域中可能存在的跨代引用。因为引用被存储在堆里,“存储屏障(store barrier)”代码将标记卡片来表示在其相应的512个字节的堆区域,可能存在从年老代到新生代的一个潜在引用。 在回收时卡片表被用于扫描跨代引用,这会为新生代有效地添加一个GC根。因此在次要回收中,一个重要的固定成本是与年老代大小成正比。

在Hotspot中,新生代有两个幸存区:交替的扮演“到达空间(to-space)”和“出发空间(from-space)”的角色。在次要回收刚开始的时候,到达空间的幸存区总是空的,扮演着次要回收中的的目标复制区域的角色。上个次要回收的目标幸存区是这次出发空间的幸存区的一部分。类似的还有伊甸区,里面的存活对象都需要复制。

次要回收的主要消耗在复制对象到幸存区和年老区上。非幸存对象在次要回收中不会被处理。次要回收的工作量直接与存活对象的数量相关,而与新生代的大小无关。Eden的大小每增加一倍,次要回收的总时间可以减少几乎一半。这样可以在内存和吞吐量中找到平衡。Eden的大小翻倍会增加每一次GC周期所占的回收时间。但是如果需要晋升的对象数量和年老代的大小是固定的,那么额外增加的时间会相对较少。

注意:在Hotspot中次要收集是个全局暂停事件。随着我们的堆越来越大,存活对象越来越多,这会变成一个很大的问题。我们已经开始看到在新生代中使用并发收集以减少暂停时间的需要。

主要回收(Major Collections)

“主要回收”是指在旧生代(old gen)上的垃圾收集,以便对象可以从年轻代晋升上来。在大多数应用程序中,绝大部分的程序状态都会在老年代里结束。最多种类的GC算法也是用在老年代上。有一些是整个空间填满时开始压缩,另一些是回收与应用程序并行以防整个空间被填满。

老年代的收集器会尝试预测什么时候需要收集,以避免年轻代的晋升失败。收集器跟踪设置在老年代上的阈值,达到法制就会进行一次回收。如果这个阈值达不到晋升条件,则触发一次“FullGC”。一次FullGC包括晋升所有年轻代中的被收集器追踪的对象,并压缩老年代。晋升失败是个非常昂贵的操作,因为这个周期里所有的状态和晋升对象都必须松开以触发FullGC。

注意:为了避免晋升失败,你需要调整你的填充空间,为老年代提供可以存放晋升后的对象(XX:PromotedPadding=<n>)。

注意:当触发FullGC时,堆可能需要增长。要避免在FullGC时调整堆大小,可以将 –Xms 和 –Xmx 设为相同的值,

与FullGC相比,一次年老代的压缩可能会是应用程序经历最久的全局暂停。压缩的时间和在年老区(tenured space)中存活对象的数量增长呈线性关系。

有时候,年老区的填充率可以通过增大幸存区(survivor spaces)大小和延长晋升到年老区前对象的存活寿命来减少。然而, 增大幸存区大小和延长晋升之前对象在次要收集 (–XX:MaxTenuringThreshold=<n>)中的存活寿命,也会增加次要回收成本和暂停时间,这样的话,幸存区之间的复制成本就会增加。

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

  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垃圾回收精粹 — Part1

    Java垃圾回收精粹分4个部分,本篇是第1部分.在第1部分里介绍了权衡点.对象生命周期以及全局暂停事件. 串行.并行.并发.CMS.G1.年轻代(Young Gen).新生代(New Gen).旧生代 ...

  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. [转载]DirectX SDK (June 2010)安装错误S1023,解决方法

    导致这个错误的原因是在安装DirectX SDK (June 2010)之前.我就安装了VS2010 . 所以也就安装了: Microsoft Visual C++ 2010 x86 Redistri ...

  2. 安装matplotlib 和Pygal

    一.  在Linux系统中安装matplotlib 如果我们使用的是系统自带的Python版本,可使用系统的包管理器来安装matplotlib,为此只需执行一行命令: $ sudo apt-get i ...

  3. [转]跟我一起写Makefile系列

    原作者:陈皓专栏 [空谷幽兰,心如皓月] 跟我一起写 Makefile(一) 跟我一起写 Makefile(二) 跟我一起写 Makefile(三) 跟我一起写 Makefile(四) 跟我一起写 M ...

  4. HTML 如何显示英文单、双引号

    // 过滤英文引号替换成中文引号 function pregstring($str){ return preg_replace('/"([^"]*)/','&quot${1 ...

  5. snort安装--daq,dnet---ERROR! dnet header not found, go get it from...等错误解决方案

    snort源码安装过程中,需要安装daq,dnet.这里想说下如何进行安装.daq简单,源码下载直接安装就可以.dnet在安装过程中,出错后总想着在网上搜一搜,结果很失望..本篇记录的不仅仅是解决安装 ...

  6. Tweet信息搜集工具tinfoleak

    Tweet信息搜集工具tinfoleak   推特是国外用户常用的社交网站.通过分析用户发布的推文以及社交活动,可以获取大量的个人信息.Kali Linux新增一款Tweet信息搜索工具tinfole ...

  7. 常见http错误码

    一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务不可用 详细分解: 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码. 代码 说明 ...

  8. hashMap原理剖析

    在日常开发中,hashMap应该算是比较常用的一个类了,今天就来学习一下hashMap的实现原理. 概念 1.什么时hash? 书面定义:就是把一个不固定长度的二进制值映射成一个固定长度的二进制值. ...

  9. Parse要垮了

    一清早收到邮件就睡不着了... 花了那么多时间熟悉api,第一个基于parse的app也要做完了... 看来国内的类似产品也不敢用了,还是老老实实用阿里云自己写backend吧...

  10. bzoj 2055: 80人环游世界 -- 上下界网络流

    2055: 80人环游世界 Time Limit: 10 Sec  Memory Limit: 64 MB Description     想必大家都看过成龙大哥的<80天环游世界>,里面 ...