《Java 虚拟机原理》5.1 GC垃圾收集及案例分析

一、GC什么对象

GC的对象是没有存活的对象,判断没有存活的对象有两种常用方法:引用计数和可达性分析

1.1 java的GCRoots引用对象

在 Java 虚拟机的语境下,垃圾指的是死亡的对象所占据的堆空间

a. 虚拟机栈中引用的对象。

b. 方法区中静态属性引用的对象。

c.方法区中常量引用的对象。

d.本地方法中JNI引用的对象。

说明:当前对象到GCRoots中不可达时候,即会满足被垃圾回收的可能。这些对象但不是就非死不可,此时只能宣判它们存在于一种“缓刑”的阶段,要真正的宣告一个对象死亡。至少要经历两次标记:

第一次:对象可达性分析之后,发现没有与GCRoots相连接,此时会被第一次标记并筛选。

第二次:对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,此时会被认定为没必要执行。

1.2 结合GC对象回顾java虚拟机内存

说明:a. 虚拟机栈中引用的对象、b. 方法区中静态属性引用的对象(b.1基本类型数据是存储在运行时常量池)、c.方法区中常量引用的对象,d.本地方法中JNI引用的对象,这些对象都存储在java堆。

图1 GC对象在java虚拟机内存图

二、什么时候GC

2.1 判断没有存活的对象有两种常用方法

如何辨别一个对象的存亡是关键问题。

1.引用计数

每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

优点:实现简单,判定效率高效,被actionscript3和python中广泛应用。

缺点:无法解决对象之间的循环引用问题。

图2 循环引用场景

2.可达性分析

目前 Java 虚拟机的主流垃圾回收器采取的是可达性分析算法。该算法的实质:将一系列 GC Roots 作为初始的存活对象合集(live set),然后从该合集出发,探索所有能够被该集合引用到的对象,并将其加入到该集合中,这个过程我们也称之为标记(mark)。最终,未被探索到的对象便是死亡的,是可以回收的。

从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的不可达对象。如下图所示,右侧的对象是到GCRoot时不可达的,可以判定为可回收对象。

图3 可达性分析

思考题:什么是GC Roots?GC Roots与GC对象的关系?

解答:由堆外指向堆内的引用,一般而言,GC Roots 包括(但不限于)下列几种,Java 方法栈桢中的局部变量已加载类的静态变量JNI handles已启动且未停止的 Java 线程。因此,GC Roots是GC对象的引用

可达性分析法的问题:在多线程环境下,其他线程可能会更新已经访问过的对象中的引用,从而造成误报(将引用设置为 null)或者漏报(将引用设置为未被访问过的对象)。误报使得Java 虚拟机损失该次垃圾回收的机会。漏报则比较麻烦,因为垃圾回收器可能回收事实上仍被引用的对象内存。一旦从原引用访问已经被回收了的对象,则很有可能会直接导致 Java 虚拟机崩溃。

2.2 触发GC的动作及时机

(1)动作:程序调用System.gc时可以触发。

(2)时机:系统自身来决定GC触发的时机

根据Eden区和From Space区的内存大小来决定,当内存大小不足时,则会启动GC线程并停止应用线程,GC又分为 Minor GC 和 Full GC。

Minor GC触发条件:①  当 Eden 区满时,触发 Minor GC。

                                  ② 当 FromSuv 或者 ToSuv  区满时,触发 Minor GC。

Full GC触发条件:  ① 调用System.gc时,系统建议执行Full GC,但是不必然执行

                                 ② Heap 的老年区空间不足

                                 ③ Metaspace 空间不足

                                 ④ 通过Minor GC后进入老年代的平均大小大于老年代的可用内存

                                 ⑤ 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

三、如何进行GC

GC算法是内存回收的理论方法,而GC垃圾收集器则是是内存回收的具体实现。下面的内容先讲GC常用算法。

3.1 GC算法理论基础

GC算法是内存回收的理论方法。GC常用算法理论有:标记-清除算法标记-压缩算法复制算法分代收集算法。即回收垃圾对象的内存共有三种方式,分别为:会造成内存碎片的清除、性能开销较大的压缩、以及堆使用效率较低的复制。目前主流的JVM(HotSpot)采用的是分代收集算法。

3.1.1 标记清除法

标记清除法是垃圾回收算法的思想基础。标记清除算法将垃圾分为两个阶段:标记阶段和清除阶段。

标记阶段:通过根节点,标记所有从根节点开始的可达对象,未标记过的对象就是未被引用的垃圾对象。

清除阶段:清除所有未被标记的对象。

图4 标记清除算法

3.1.2 复制算法

复制算法是,将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在适用的内存中存活对象复制到未使用的内存块,然后清除使用的内存块中所有的对象。

图5 复制算法

3.1.3 标记压缩算法

标记压缩算法是一种老年代的回收算法。

标记阶段:与标记清除算法一致,对可达对象做一次标记。

清理阶段:为了避免内存碎片产生,将所有的存活对象压缩到内存的一端。

图6 标记压缩算法

四、Java虚拟机的堆划分

Java 虚拟机将堆划分为新生代老年代。其中,新生代又被划分为 Eden 区,以及两个大小相同的 Survivor 区即FromSuvToSuv

当调用new 指令时,java虚拟机在Eden区中划出一块作为存储对象的内存。由于堆空间是线程共享的,因此直接在Eden区是需要进行同步的。new 指令,便可以直接通过指针加法(bump the pointer)来实现,即把指向空余内存位置的指针加上所请求的字节数。

问题1:两个线程同时new Object1对象,则堆如何划分内存?

解答:由于堆内存是线程共享的,同步为两个线程分别划分object1的内存空间,即有2个object1对象。该技术被称为TLABThread Local Allocation Buffer,对应虚拟机参数 -XX:+UseTLAB,默认开启)。

问题2:当 Eden 区的空间耗尽了怎么办?

解答:这个时候Java虚拟机会触发一次Minor GC,来收集新生代的垃圾。存活下来的对象,则会被送到Survivor区

问题3:新生代的两个Survivor 区,即FromSuvToSuv有什么用处?

解答:当Minor GC时,Eden和FromSuv中的存活对象会被复制到ToSuv中,然后交换FromSuv和ToSuv指针,以保证下一次Minor GC时,ToSuv还是空的。满足两种情况之一,可以使对象移动到老年代:1. Minor GC,存活对象从FromSuv复制到ToSuv,其对象的age+1,当超过(默认值)15的时候,转移到老年代;2. 动态对象,如果survivor空间中相同年龄所有的对象大小总和,大于survivor空间的一半,则年级大于或等于该年级的对象就可以直接进入老年代

注意Minor GC只针对新生代进行垃圾回收,所以在枚举 GC Roots 的时候,需要考虑从老年代到新生代的引用。为了避免扫描整个老年代,Java 虚拟机引入卡表(Card Table)的技术,大致地标出可能存在老年代到新生代引用的内存区域

五、GC案例分析

从一个object1分析该对象在分代垃圾回收算法中的回收轨迹

Minor GC是指发生在新生代的GC,因为Java对象大多是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快;Full GC是指发生在老年代的GC,出现Full GC一般会伴随至少一次的Minor GC,其速度一般比Minor GC慢10倍以上。

步骤1:实例化object1,出生于新生代的Eden区域

步骤2Minor GC,object1移动到新生代的Fromsuv区域,object1还存活。

步骤3Minor GC,通过复制算法将object1移动到新生代的ToSuv区域,同时object1的年龄age+1,object1 依然存活;

步骤4Minor GC,在新生代的survivor区域中,与object1同龄的对象并没有达到survivor的一半。因此,通过复制算法将FromSuv和ToSuv 区域进行互换,object1对象被移动到了新生代的ToSuv,object1 依然存活;

步骤5Minor GC,此时survivor中和object1同龄的对象已经达到survivor的一半以上,object1被移动到了老年代区域,object1 依然存活。

满足两种情况之一,都可以使object1对象移动到老年代:

1. Minor GC,存活于survivor 区域的object1对象的age+1,当超过(默认值)15的时候,转移到老年代;注意:minor GC下,步骤2/3/4中的移动/复制全部Tosuv/Fromsuv区域的对象。

2. 动态对象,如果survivor空间中相同年龄所有的对象大小总和,大于survivor空间的一半,则年级大于或等于该年级的对象就可以直接进入老年代

步骤6Full GC会触发stop the world。object1存活一段时间后,此时GC Roots不可达object1,而且此时老年代空间比率已经超过了阈值,触发了Full GC,此时object1被回收。

注意:object1 被回收的必要条件是 object1 不可达(GC Roots),即 object1 的引用是弱引用

以上的步骤采用分代垃圾收集的思想,描述object1对象从存活到死亡的过程。新生代:采用复制算法,老年代:采用标记-清除算法或者标记-整理算法

 stop the world是一种简单除暴的方式,即停止其他非垃圾回收线程的工作,直到完成垃圾回收。Java 虚拟机中的 stop the world 是通过安全点(safepoint)机制来实现的。当 Java 虚拟机收到 Stop-the-world 请求,它便会等待所有的线程都到达安全点,才允许请求 stop the world 的线程进行独占的工作。安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。例如,java执行某个JNI本地方法时,不访问Java对象、调用Java方法、返回至原Java方法,则Java虚拟机的堆栈不会发生改变,所以这段代码可以作为安全点。因此,Java虚拟机在这个安全点,可以同时进行垃圾回收和执行这段代码

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

推荐阅读更多精彩内容