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. poj 2828(线段树单点更新)

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 18561   Accepted: 9209 Desc ...

  2. WordPress 用户管理插件 WP User Manager

    WP User Manager 是一个较新的用户管理插件,可以在前端实现 用户注册.登录.找回密码.修改个人资料.修改密码等功能,如果你在找这方面的插件,WP User Manager 应该是一个不错 ...

  3. IOS - Safari中click点击事件无效

    做web移动端页面时,安卓端一点问题也没,发现在ios真机上点击事件无效,发现Safari下只有默认可点击的元素才click点击事件,像span div等元素是不具有点击事件的. 解决问题四种方式: ...

  4. es6 class 中 constructor 方法 和 super

    首先,ES6 的 class 属于一种“语法糖”,所以只是写法更加优雅,更加像面对对象的编程,其思想和 ES5 是一致的. <1>constructor function Point(x, ...

  5. 前端代码编辑器ace 语法验证

    本文主要是介绍实际项目中如何加入语法检测功能.官方文档链接https://github.com/ajaxorg/ace/wiki/Syntax-validation 代码编辑器ace,使用webwor ...

  6. Rookey.Frame v1.0快速开发平台-用户登录

    上一次介绍的了Rookey.Frame v1.0快速开发平台的整体功能,接下来会对各个功能点进行解析说明,今天给大家介绍下系统登录功能. 用户登录 系统中基本上所有功能页面都是从后台代码拼接后返回的, ...

  7. elementUI 学习入门之 inputNumber 计数器

    InputNumber 计数器 基础用法 <el-input-number v-model="num2"></el-input-number> v-mode ...

  8. nyoj 214 单调递增子序列(二)

    单调递增子序列(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 ,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度. ...

  9. Python操作Mongo数据库

    连接数据库 import pymongo # 连接到数据库,以下两种方式均可 client = pymongo.MongoClient(host='localhost', port=27017) cl ...

  10. luoguP3232 [HNOI2013]游走 贪心 + 概率期望 + 高斯消元

    首先,题目中的无向简单连通图代表着没有自环,重边... 总分的期望 = 每条边的期望之和...................每条边的期望又可以拆成$u \to v$的期望和$v \to u$的期望 ...