Java垃圾收集器与内存分配策略

Java垃圾收集器与内存分配策略

image.png
  • 程序计数器,虚拟机栈,本地方法栈3个区域的内存随线程而生,随线程而灭,每一个栈桢分配多少内存在类结构确定下来时就已知的,因此具备确定性,不需要过多考虑回收的问题,方法/线程结束时内存自然就跟着回收了
  • Java堆,方法区一个接口多个实现类需要的内存不一样,一个方法的多个分支需要的内存也不一样.运行时才知道会创建哪些对象,这部分内存的分配和回收也是动态的。垃圾收集器所关注的是这部分内存
image.png

对象已死吗

<font color=red>垃圾收集器在对堆回收前,要确定哪些对象还存活,哪些对象已死去</font>

引用计数法

  • 引用计数法
  • 缺点: 很难解决对象之间相互循环引用的问题,所以主流JVM没有选用该方法管理内存

可达性分析算法

  • 基本思想: 通过一系列"GC Roots"的对象作为起点,开始向下搜索,走过的路径称为引用链(Reference Chain),当一个对象不可达时,则证明该对象不可用
  • 可达性分析算法
  • 可作为GC Roots的对象包括
    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 本地方法栈中Native方法引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
  • 对象的finallize()方法只会被系统自动调用一次,下次回收时不会调用了。该方法由JVM自动建立的,低优先级Finalizer线程执行,但并不承诺会等待它运行结束,以防止finallize存在死循环导致F-Queue队列中的其它对象永久等待.
  • finallize()运行代价高昂,不确定性大,不鼓励使用
  • 枚举根节点(GC Roots)
    • 问题点: 消耗很多时间
      • 很多应用方法区就有数百M,逐个检查耗时
      • GC停顿: GC时必需停顿所有Java执行线程(Stop the World), 防止分析过程中对象引用关系还在不断变化
    • 解决方案: 主流JVM(如HotSpot)使用准确式GC,HotSpot使用一组OopMap的数据结构.
      • 类加载完成时: 把对象内什么偏移量上是什么类型的数据计算出来
      • JIT编译时: 在<font color=red>特定的位置(安全点)</font>记录下栈和寄存器中哪些位置是引用
      • 如果为每条指令都生成OopMap,GC的空间成本会变得很高,另外OopMap可能会导致引用关系变化,而GC时需要引用关系不能变化 解决方案是在<font color=red>特定的位置设置安全点,和安全区域</font>,只有在安全点才能GC停顿.
      • 安全点的选定标准:是否具有让程序长时间执行的特征.即指令序列复用,例如:方法调用,循环跳转,异常跳转等
      • GC时如何让所有线程都跑到最近的安全点上再停顿下来?
        • 方案一: 抢占式中断(已淘汰),GC时先把所有线程全部中断, 如发现有线程中断的地方不在安全点上,就恢复线程,让它跑在安全点上
        • 方案二:主动式中断, GC需要中断线程时,不直接对线程操作,仅仅简单设置一个标志,各个线程执行时主动去轮询(安全点+创建对象需要分配内存的地方进行轮询)这个标志,发现中断标志为true时,自己就中断挂起。
          • 主动式中断
      • 如果程序不执行(Sleep,Blocked),则无法进入安全点进行GC停顿,这时候需要安全区域来解决. 安全区域是指在一段代码片段中,引用关系不会发生变化.
        • 线程执行到安全区域中的代码时, 标记自己已进入了Safe Region.
        • JVM GC时不管标记自己为Safe Region的线程
        • 线程离开Safe Region时,检查系统是否已完成GC,如果没有,则等待直到收到可以安全离开Safe Region的信号为止
          • 安全区域

引用

引用 说明 举例
强引用 垃圾收集器<font color=red>永远不会回收</font>强引用的对象 Object obj = new Object()
软引用 <font color=red>OutOfMemory异常前</font>回收
弱引用 <font color=red>下次垃圾收集发生前</font>回收
虚引用 唯一目的是对象被收集器回收时收到一个系统通知.
不会对对象生存时间构成影响, 也无法通过虚引用获得对象的实例
-

垃圾收集算法

标记-清除算法

  • 两个阶段
    • 标记: 先按可达性算法标记出所有需要回收的对象
    • 回收: 标记完成后统一回收,直接对可回收对象进行清理
  • 缺点
    • 标记回收两个过程效率都不高
    • 会产生大量不连续的内存碎片
  • 标记-清除算法

标记-整理算法

  • Mark-Compact
  • 用于<font color=red>回收老年代</font>
- 标记过程 回收过程
标记-清除算法 先按可达性算法标记出所有需要回收的对象 直接对可回收对象进行清理
标记-整理算法 同上 让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
  • 标记整理算法

复制算法

  • 将可用内存按容量划分为大小相等的两块,每次只使用其中一块.当一块内存用完,将存活对象复制到另一块上,然后再把已使用过的内存一次清理掉.
  • 复制算法
  • 解决了效率问题
  • 用于<font color=red>回收新生代</font>
  • 代价是内存缩小了一半
  • IBM研究表明新生代中98%的对象是朝生夕死的,所以不需要按1:1比例划分内存空间,而是将内存划分为一块较大的Eden空间和2块较小的Survivor空间,每次使用Eden和Survior-1.回收时将Eden和Survior-1复制到Survior-2,如果Survior-2空间不够,则使用担保内存(要还的).

分代收集算法

分代收集算法

垃圾收集器

  • HotSpot JVM的垃圾收集器
  • 发展历程: Serial->Parallel->CMS->G1 注意:没有万能的收集器,只有最合适的

Serial 收集器

  • 新生代收集器
  • 单线程:进行垃圾收集时,必需暂停其它所有的工作线程(Stop The World). 即便是更加先进的收集器,也只能不断缩短用户线程停顿时间,而不能完全消除
  • 优点: 简单高效,没有线程交互的干扰,专心做垃圾手机
  • 适合运行Client模式下的虚拟机(桌面应用场景下分给JVM管理的内存不大,停顿时间几十ms-100多ms,可以接受)
  • Serial+Serial Old收集器

ParNew 收集器

  • 新生代收集器
  • Serial收集器的多线程版本
  • Server模式下JVM的首选新生代收集器(除了Serial外,只有它能与CMS配合工作)
  • 多CPU下效果较好,1-2个CPU效果不一定比Serial好
  • ParNew+Serial Old收集器

Parallel Scavenge 收集器

  • 与ParNew一样, 特点是它的关注点与其他收集器不同.
收集器 关注点 适合
CMS等其他收集器 尽可能缩短垃圾收集时用户线程的停顿时间
Parallel Scavenge 达到可控制的吞吐量 停顿时间越短,越适合用户交互程序|高吞吐量可高效利用CPU时间,尽快完成程序运算任务,适合后台运算而不需要太多交互的任务
  • 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间) JVM总共运行100min,垃圾收集1min,吞吐量=99%
  • GC自适应调节策略:JVM根据系统运行情况收集性能监控信息,动态调整参数以提供合适的停顿时间或最大的吞吐量, 是与ParNew的一个重要区别

Serial Old 收集器

  • Serial 的老年代版本

Parallel Old 收集器

  • Parallel的老年代版本
  • Parallel Scavenge+Parallel Old
收集器 线程 年代 关注点
Serial 单线程 新生代 关注用户线程的停顿时间
Serial Old 单线程 老年代 关注用户线程的停顿时间
ParNew 多线程 新生代 关注用户线程的停顿时间
Parallel Scavenge 多线程 新生代 <font color=red>关注吞吐量</font>
Parallel Old 多线程 老年代 <font color=red>关注吞吐量</font>
CMS 多线程 老年代 关注用户线程的停顿时间

CMS收集器

  • Concurrent Mark Sweep
  • 多线程
  • 老年代收集器
  • 以获取最短回收停顿时间为目标的收集器
  • 适用于互联网站或B/S系统的服务端
  • 基于标记-清除,但更复杂,有4个步骤

|阶段序号|阶段|描述|速度|Stop The World|(非并行)|
|----|----|----|----|
|1|初始标记|仅仅只是标记一下能直接关联到对象的GC Roots|很快|是|否|
|2|并发标记|GC Roots Tracing的过程,标记GC链中的对象|很快|否|是|
|3|重新标记|修正并发标记期间因用户程序继续运作而导致标记产生变动的那部分对象的标记记录|初始标记<重新标记<并发标记|是|是|
|4|并发清除||耗时较长|否|是|

  • CMS收集器
  • 缺点

    • 对CPU资源非常敏感,并发阶段占用线程(CPU资源)而导致应用程序变慢,总吞吐量降低
    • 无法处理浮动垃圾:并发清理阶段用户线程还在运行,伴随新垃圾产生,这部分垃圾在标记过程之后,无法当次集中处理,只好下次再处理
    • "标记-清除"算法导致了大量空间碎片

G1收集器

  • Garbage-First

  • 收集器技术发展的最前沿成果之一,正式商用Since JDK1.7

  • 特点

    • 并行与并发: 能充分利用多CPU硬件优势,其他收集器需要Stop The World的地方, CMS依然可以并发执行
    • 分代收集: 即可以收集新生代也可以收集老年代.无须和其他收集器配合
    • 空间整合:整体基于"标记-整理"算法,局部基于"整理算法",不会产生内存碎片
    • 可预测停顿: 能让使用者明确在长度为M ms的时间片段内,消耗在垃圾收集上的时间不超过N ms
  • G1将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留了新生代和老年代的概念,但不再物理隔离了,它们都是一部分Region的集合

  • 可预测停顿是因为G1通过跟踪各Region里垃圾堆积的价值大小,维护优先级列表,根据允许的收集时间,优先回收价值最大的Region(Grabage-First的由来),从而避免了在整个Java堆中进行全区域的垃圾收集

  • G1把内存"化整为零"的思路实现并不简单,因为Region并不是孤立的,避免全堆扫描的解决思路如下:

    1. 每个Region有个Remembered Set与之对应
    2. JVM发现程序在对Reference类型的数据进行写操作时, 会产生一个Write Barrier暂时中断写操作
    3. 检查Reference引用的对象是否处于不同Region中
    4. 是则通过CardTable将相关Reference记录到被引用对象所属Region的Remembered Set中
    5. 当内存回收时,GC Roots的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏
  • 不计算Remembered Set的操作, G1 运作的大致步骤如下:

阶段序号 阶段 描述 速度 Stop The World 并发执行(非并行)
1 初始标记 仅仅只是标记一下能直接关联到对象的GC Roots 很快
2 并发标记 GC Roots Tracing的过程,标记GC链中的对象 很快
3 最终标记 修正并发标记期间因用户程序继续运作而导致标记产生变动的那部分对象的标记记录 初始标记<重新标记<并发标记 可并行
4 筛选回收 对各Region回收价值和成本进行排序,根据用户所期望的GC停顿时间来指定回收计划 耗时用户可控制
  • G1收集器

内存分配与回收策略

  • 分配策略
  • 空间分配担保

    空间分配担保
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容