Java虚拟机垃圾收集机制

垃圾收集机制

在虚拟机内存模型中:

  1. 程序计数器,消耗内存可以忽略不计
  2. 虚拟机栈,在编译期可知需要分配多少内存空间,栈帧入栈分配空间,出栈回收内存。
  3. 本地方法栈,与虚拟机栈基本一致。
  4. Java堆,最大的内存区域,对象几乎在运行时才分配内存,创建频繁甚至需要同步分配,所以堆内存自动回收机制特别重要。
  5. 方法区,同样需要内存回收。

本章了解整个垃圾收集机制

  1. 了解垃圾收集流程
  2. 重点掌握虚拟机垃圾收集算法:
    • 对象存活判定算法
    • 垃圾收集算法
  3. 虚拟机GC算法实现
  4. 垃圾收集器
  5. 对象在GC时如何分配

对象存活判定算法

什么时候发生GC

当分配Java对象内存时,Java堆内存空间不够,且无法扩展时,进行一次GC

GC处理的是Java堆中的对象,哪些对象是需要回收的呢

这就需要对象存活判定算法,目前有两种对象存活判定算法

  1. 引用计数法
  2. 可达性分析算法

引用计数法是什么

对象中设置一个引用计数器,每当有一个地方引用它时,计数器值加1。引用失效时,计数器值减1。

计数器值为0的对象就是可被回收的对象。

引用计数法有什么优缺点

  • 优点:实现简单,判定效率高
  • 缺点:无法解决对象直接相互循环引用的问题

什么是对象间的相互循环引用

例如:对象A和对象B都有字段ref, 现在让A.ref=B, B.ref=A

然后手动执行GC, 虽然这两个对象不可能再被访问,但他们之间的相互引用让引用计数器都不等于0,无法被回收。

java虚拟机使用的是引用计数法吗

不是,因为对象直接相互循环引用的问题,java虚拟机使用的是可达性分析算法

什么是可达性分析算法

通过一系列的GC Roots的对象作为起始点,从这些起始点开始向下搜索,搜索走过的路径被称为引用链

当一个对象到GC Roots没有任何应用链相连,则证明对象是不可用的。

也可以说,从GC Roots到对象不可达时,则对象可回收。

一系列的GC Roots对象,GC Roots对象有哪些

  1. 虚拟机栈中引用的对象,即栈帧中本地变量表中对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI,即Native方法中引用的对象。

当GC Roots到对象不可达时,对象就一定可以被回收吗

当GC Roots到对象不可达时,对象不一定会被回收,需要经历两次标记。

  1. 第一次GC Roots 到对象不可达时,对象被标记1次。
  2. 判断对象是否覆盖了finalize()方法
    • 对象覆盖了finalize()方法,判断虚拟机是否执行了finalize方法
      • 虚拟机没有执行finalize方法,将对象放入F-Queue队列中,待回收
        • 稍后虚拟机自动建立低优先级的Finalizer线程执行F-Queue队列中对象的finalize方法。
          • finalize方法中对象与引用链上链接建立关联,即GC Roots到对象可达
          • finalize方法中对象没有雨引用链上链接建立关联
        • 稍后GC对F-Queue队列中对象进行第二次标记
          • 与引用链建立链接的对象,移除待回收集合
          • 没有与引用链建立链接的对象,对象可回收
      • 虚拟机已经执行了finalize方法,对象可回收
    • 对象没有覆盖finalize()方法,对象可回收

虚拟机自动建立的低优先级的Finalizer线程执行的时间很长怎么办

Finalizer线程中对象finalize方法可能并不会等待方法执行结束,因为finalize方法可能出现死循环等异常情况,导致整个内存回收机制崩溃。

所以只要执行了finalize方法的对象,且没有与引用链建立关联,对象就是可回收的。虽然可能方法没有结束。

对象的finalize方法有点像C++的析构函数呀

是的,finalize方法就是Java对C++做出的妥协。
尽量不要使用这个方法,try-catch-finally可以做的更好。

方法区或永久代也可能被GC吗

方法区或永久代也会被GC, 虽然效率会很低。

方法区或永久代会回收两部分内容:

  1. 废弃常量
  2. 无用的类

这里的常量是指什么

这里是指运行时常量池中常量:字面量和符号引用

常量如何判断可回收

  1. 堆中没有对象引用该字面量或符号引用
  2. 其他地方也没有引用该字面量或符号引用

jdk1.8移除了永生代,会有什么变化吗

常量被移到堆中,即常量的回收与对象的回收一致了。
所以jdk1.8只负责类回收。

类如何判断可回收

条件苛刻,下面3个条件同时满足才可以回收

  1. 该类所有实例被回收,即java堆中不存在该类的任何实例
  2. 加载该类的ClassLoader已经被回收
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

而且是否对类进行回收,虚拟机还提供参数进行控制:-Xnoclassgc, 是否开启类回收。

总结

对象存活判定算法

  1. 引用计数法
  • 原理:对象中设置一个引用计数器,当有地方引用它时,计数器+1,引用失效时,计数器-1,当计数器==0时,对象已死。
  • 优点:实现简单,判定效率高
  • 缺点:无法解决对象间直接相互循环引用问题。
  1. 可达性分析算法
  • 原理:一系列的GC Roots对象作为起始点,向下搜索,走过的路径被称为引用链。当一个对象到GC Roots没有一个引用链相连,或者说GC Roots到对象不可达时,对象已死
  • 优点:解决对象间直接相互循环引用的问题
  • GC Roots对象:
    1. 虚拟机栈中引用的对象,即栈帧中本地变量表的引用对象
    2. 本地方法栈中JNI,即Native方法中对象
    3. 方法区中静态变量引用的对象
    4. 方法区中常量引用的对象

对象两次标记判定算法

  • 原理:对象的fianlize方法可以第一次标记已死的对象重新存活。
  • 步骤:
    1. 判断对象是否覆盖finalize方法或者判断虚拟机是否已经执行了finalize方法
    2. 如果没有覆盖finalize方法或已经执行了finalize方法,则对象必死
    3. 如果对象覆盖了finalize方法且虚拟机没有执行finalize方法,将对象放到F-Queue队列中
    4. 稍后虚拟机自动建立低优先级的线程,执行F-Queue队列中对象的finalize方法,因虚拟机资源和效率,不一定会等待所有对象的finalize方法执行完成。
    5. 稍候GC对F-Queue队列中对象进行第二次标记,如果对象在finalize方法中与引用链重新建立链接,即第二次标记存活的对象,移出待回收集合。如果第二次标记还是死亡,则对象必死。

方法区回收判定算法

  1. 常量判定
    • 常量内容:字面量和符号引用
    • 判定算法:
      • 堆中没有对象引用该字面量或符号引用
      • 其他地方没有引用该字面量或符号引用
  2. 类判定
    • 虚拟机启用类回收卸载
      • 使用参数:-Xnoclassgc,启用类回收卸载
    • 类信息同时满足3个条件判定可卸载
      • 堆中没有该类的任何实例对象
      • 该类的ClassLoader被卸载
      • 该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射机制引用到该类

垃圾回收算法

垃圾回收算法有哪几种

  1. 标记清除
  2. 复制算法
  3. 标记整理
  4. 分代收集

什么是标记清除算法

  1. 标记:标记出所有待回收的对象。
  2. 清除:标记完成,统一回收被标记对象的内存。

标记清除算法有什么优缺点

  • 优点:实现简单
  • 缺点:
    1. 标记和清除的效率都不高。
    2. 一次GC后会产生大量的不连续的内存碎片,当分配大对象时,可能会无法分配内存而重新发起一次GC

什么是复制算法

  1. 将堆内存分为大小相等两块区域,每次只用其中一块
  2. 当使用的那一块内存使用完毕,触发一次GC, 将存活的对象复制到另外一块,已使用过的内存空间一次清理。
  3. 复制时只移动堆顶指针,按顺序分配内存,不会有大量内存碎片出现

复制算法有什么优缺点

  • 优点:解决了标记清除算法的效率问题和内存碎片问题
  • 缺点:一次只能用一半的内存空间,空间消耗太大

复制算法如何优化内存空间消耗太大的问题

IBM的研究表明: 98%的对象朝生夕死,熬过一次GC的对象极少,所以并不需要按1:1等比例来划分内存空间。

内存划分:

  1. Eden空间:占内存80%,数量1
  2. Survivor空间:占内存10%,数量2
    • From空间
    • To空间

每次使用Eden空间和Survivor空间中一个,即使用90%的空间,剩余10%的空间。
毕竟98%只是一般理论数据,10%的空间足够存放理论上2%的存活对象。

发生GC时,将存活对象(理论上2%)复制到Survivor的另一块空闲空间,Eden空间和已使用的一块Survivor空间回收内存。

98%毕竟是理论值,如果超过10%的对象熬过GC, 特别是大对象,那该怎么办

98%的对象朝生夕死,根据对象生存周期的不同可以将java堆内存分为两种:新生代和老年代

开始内存都在新生代中分配,每熬过一次GC, 对象年龄+1,当对象年龄到15岁时,移到老年代。

很显然,新生代的对象适合用复制算法。但如果10%的空间不够,会用老年代的空间进行担保,进入老年代的空间。

能熬过15次GC的老年代中对象,存活率肯定比较高,用复制算法的10%空间根本不够吧

是的,老年代中对象存活率很高,不适合使用复制算法,所以使用标记整理算法。

什么是标记整理算法

  1. 标记:对所有可回收的对象进行标记
  2. 整理:标记完成后,所有存活的对象向一端移动,然后直接清理存活对象边界之外的内存空间。

标记整理算法有什么优缺点

  • 优点:解决了标记整理的大量内存碎片的问题,适合老年代。老年代对象存活率高,没有额外空间对它进行分配担保。
  • 缺点:不适合新生代。新生代对象存活率低,没有复制算法效率高。

那新生代就用复制算法,老年代就用标记整理算法就好了

是的,这就是分代算法

  1. 新生代对象存活率低,使用复制算法,只需复制少量对象就完成收集。
  2. 老年代存活率高,且没有额外空间担保,必须用标记整理算法回收空间。

总结

堆内存分代

  • 原因:98%的对象朝生夕死
  • 分代:
  1. 新生代
    • 存放对象:对象起始都在新生代分配,每熬过一次GC,对象年龄+1,对象到15岁,移到老年代。
    • 复制算法空间划分:使用复制算法对新生代空间划分
      • Eden空间:占用新生代80%,1个,使用的空间。
      • Survivor空间:占用新生代20%,2个,使用其中之一。
        • From空间,占用新生代10%
        • To空间,占用新生代10%
  2. 老年代

垃圾收集算法

  1. 标记清除
    • 原理:标记所有待回收的对象,标记完成,清理所有待回收对象内存空间。
    • 优点:实现简单
    • 缺点:标记和清理效率不高,而且产生大量内存碎片。
  2. 复制算法
    • 原理:
      • 将新生代分为两部分空间,一块Eden空间(占用80%空间)和两块Survivor空间(分别占用10%空间),每次只使用Eden空间和一块Survivor空间,即使用90%空间。
      • 发生GC时,将存活的对象复制到另一块空闲的Survivor空间,Eden空间和已使用的Survivor空间一次清理。
    • 优点:适合收集新生代朝生夕死的对象,只需复制少量的存活对象完成收集,且不会产生大量内存碎片。
    • 缺点:存活对象超过10%时,需要担保进入老年代。
  3. 标记整理
    • 原理:标记所有待回收对象,标记完成,将存活对象移到一端,端边界之外的内存空间一次清理。
    • 优点:适合对象存活率高,且无法担保的老年代。也不会产生大量内存碎片。
    • 缺点:标记和整理效率不高。
  4. 分代算法
    • 原理:在新生代使用复制算法,只需复制少量对象即可完成收集。在老年代使用标记整理算法,老年代对象存活率高,且无法担保。

想共同学习jvm的可以加我微信:1832162841,或者进QQ群:982523529

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

推荐阅读更多精彩内容