11 | 垃圾回收(上)
11 | 垃圾回收(上)
讲述:郑雨迪
时长12:12大小5.59M
引用计数法与可达性分析
Stop-the-world 以及安全点
垃圾回收的三种方式
总结与实践
赞 14
提建议
精选留言(72)
- 钱置顶2018-08-16非常感谢,此篇可用通俗易懂来形容,其他同学问的问题也很棒! 小结: 1:垃圾回收-核心工作就是回收垃圾,哪关键点回来了。什么是垃圾?这个垃圾需要分类嘛?怎么定位垃圾?怎么回收垃圾?回收垃圾的方法都有哪些?他们都有什么优缺点?另外,就是我们为什么要学习垃圾回收? 2:站在JVM的视角来看 垃圾-就是无用对象所占用的堆内存空间 貌似不需要垃圾分类,识别垃圾并回收就行 定位垃圾,是垃圾回收的关键点 晚安💤,明天继续写展开30
- 旭东(Frank)置顶2018-08-28赞,这种循序渐进的讲法,不知道了怎么工作,还知道了为啥要设计成这样,Why和what都和谐的在一起讲了17
- 钱2018-08-17非常感谢,此篇可用通俗易懂来形容,其他同学问的问题也很棒! 小结: 1:垃圾回收-核心工作就是回收垃圾,哪关键点回来了。什么是垃圾?这个垃圾需要分类嘛?怎么定位垃圾?怎么回收垃圾?回收垃圾的方法都有哪些?他们都有什么优缺点?另外,就是我们为什么要学习垃圾回收? 2:站在JVM的视角来看 垃圾-就是无用对象所占用的堆内存空间 垃圾分类-貌似不需要垃圾分类,识别垃圾并回收就行 定位垃圾-是垃圾回收的关键点,无用的对象占用的堆空间即是垃圾,那就需要先定位无用的对象,这里的无用是不再使用的意思,咋判断呢?文中介绍了两种方法,计数法和标记法(祥看原文)核心在于能定位出无用的对象,后出现的方法往往比早出现的更好一点,这里也一样,标记法能解决计数法,解决不了的循环引用不能回收的问题,但是也存在其他的问题,误报和漏报的问题,误报浪费点垃圾回收的机会浪费点空间,漏报在多线程并发工作时可能会死JVM的,所以,比较严重,所以,JVM采用了简单粗暴的stop-the-world的方式来对待,所以,老年代的回收有卡顿的现象 怎么回收垃圾-定位出垃圾,回收就是一个简单的事情了,当然也非常关键,把要回收的堆内存空间标记为可继续使用就行,下次有新对象能在此空间创建就行 回收垃圾的方法-文中介绍了三种,清除、压缩、复制 清除法-简单,但易产生碎片,可能总空间够但分配不了的问题 压缩法-能解决清除法的问题,但是复杂且耗性能 复制法-折衷一些,但是空间利用率低,总之,各有千秋 为什么要学-这个最容易,因为面试需要、装逼需要、升职加薪需要、人类天生好奇、还有免于被鄙视及可以鄙视其他人展开
作者回复: 赞!
共 6 条评论43 - Geek_488a8e2018-08-31误报和漏报,我觉得可惜这样理解,垃圾回收是先标记活的对象,后回收死的对象,那么如果标记好后,其它线程产生了垃圾,即将活的变死了,这种内存是不会释放的。另外,如果这时产生了新对象,由于没被标记为活的,所以被释放了,这就危险了39
- 茶底2018-08-15老师下一期能讲一下g1算法吗。讲深一点😁32
- 钱2018-08-17疑问❓ 1:JVM的stop-the-world机制非常不友好,有哪些解决之道?原理是什么? 2:压测时出现频繁的gc容易理解,但是有时出现毛刺是因为什么呢? 3:fullgc有卡顿,对性能很不利,怎么避免呢?
作者回复: 1. 采用并行GC可以减少需要STW的时间。它们会在即时编译器生成的代码中加入写屏障或者读屏障。 2. Y轴应该是时间,那毛刺就是长暂停。一般Full GC就会造成长暂停。 3. 通过调整新生代大小,使对象在其生命周期内都待在新生代中。这样一来,Minor GC时就可以收集完这些短命对象了。
共 3 条评论27 - suynan2019-03-06安全点的这个地方,看得我是一脸懵逼共 1 条评论23
- Leon Wong2018-09-11老师你好,例子里的foo方法中的for循环,其中i变量类型我从int型改成long型后,长暂停的现象不存在了,请问是为何?
作者回复: 这是C2一个诡异的地方。 for (int i=start; i<limit; i++) {..} 对于int类型的循环变量i,如果满足 1) 基于该循环变量的循环出口只有一个,即i < limit,2) 循环变量随着迭代的增量为常数,例子中i++即增量为1,以及循环变量的上限(当增量为负数时则是下限)为循环无关的,即limit应是循环无关,那么C2会将其判断成计数循环(counted loop),然后默认不插入safepoint。 而对于long类型的循环变量,C2直接识别为非计数循环,需要插入safepoint。
共 3 条评论15 - 浪迹江湖2018-09-26突发奇想:如果 GC 将引用计数算法和可达性分析算法结合起来使用会怎样? 循环引用毕竟是少数,如果先用引用计数算法回收掉大部分对象,再对剩余的小部分对象采用可达性分析算法解决循环引用问题。可能比只使用可达性分析算法带来更好的回收效率。
作者回复: 赞想法!不过我认为没有达到更好的回收效率,因为垃圾回收标记的是非垃圾,剩余没有标记的对象是垃圾。用引用计数法清理后,可达性分析仍需遍历所有活着的对象。 但是可以将引用计数做成minor minor GC,只有当引用计数回收不了垃圾时,再触发可达性分析。感兴趣的话可以深入探索一下业界其他非Java runtime的垃圾回收算法。
11 - jiaobuchongจุ๊บ2019-02-171、文中所说的误报和漏报是不是说反了啊, 并发环境下标记完后,线程将引用改成 null,导致损失了部分垃圾回收的机会,这是属于漏报吧? 已标记,然后将引用设置为未被访问过的对象,导致回收了仍被引用的内存,这个属于误报吧? 2、在标记的过程中,是不是只需要记录存活的对象就行,不用标记垃圾对象,后续在执行回收算法的时候,也只是在操作已经标记的存活的对象?展开共 3 条评论8
- 素丶2019-08-09结合 Rx 的回答容易帮助理解。 https://www.zhihu.com/question/53613423/answer/1357432588
- javaadu2018-12-13@茶底,这是我写的G1学习笔记:https://www.jianshu.com/p/a3e6a9de7a5d共 1 条评论6
- WolvesLeader2018-08-17很是不明白,我的理解有没有stop the word 是和垃圾回收器有关的,看完之后怎么觉得您的意思是,不管什么垃圾回收器都会出现stop the word
作者回复: 目前的垃圾回收器多多少少需要stop the world,但都在朝着尽量减少STW时间发展。 完全的并发GC算法是存在的,但是在实现上一般都会在枚举GC roots时进行STW。
7 - 正是那朵玫瑰2018-08-15老师有几个不明白的地方,误报和漏报不太明白: 1、假设A引用开始指向A1对象:A------>A1,按老师说的误报就是将引用A指向null:A------>null,那么此时A1对象不是没有引用了,不就可以垃圾回收了么,为什么会错过垃圾回收的机会呢? 2、漏报,是将A引用指向一个未被访问的对象假设对象为B:A----->B,此时A引用原来指向的对象应该没有引用了吧,为什么会垃圾回收器可能会回收事实上仍被引用的对象呢?展开
作者回复: 这里指的是,GC已经标记完成,然后其他线程进行修改的情况(也是并发GC所要解决的问题)。 当GC标记完成,还未开始回收时,你更新了其中一个引用,使之指向null,那么原来指向的对象本可以被回收的。 如果指向一个新的对象,这个对象可没有被标记为不能回收,垃圾回收器就直接给回收掉了
6 - Jussi Lee2018-09-29一、垃圾回收算法 1、引用计数法(文中已经介绍,主要的缺点是无法处理循环引用;在每次引用的产生和消除的时候,会伴随着一个加法或者减法的操作,对性能有一定的影响) 2、标记清除法(从根节点出发开始所有可达的对象,未被标记的就是垃圾对象。主要缺点是产生空间碎片) 3、复制算法(将原空间分为两块,每次使用其中一块,在垃圾回收时,进行复制,然后转换使用的内存空间。主要的缺点是将系统的内存折半。主要适用于存活对象少,垃圾对象多的情况下) 4、标记压缩法(从根出发对所有可达对象进行一次标记,然后进行压缩。最后进行清理) 5、分代算法(每一种垃圾回收算法都有其优缺点。分代算法是根据对象的特点分成几块,新建的对象放入新生代区域,当一个对象经历了几次复制后还存活则放入老年代。老年代因为对象存活率高复制算法不适用,因此采取标记清除或者标记压缩) 6、分区算法(把堆空间划分为连续的不同小区间。降低了GC产生的影响)展开6
- 彩色的沙漠2018-08-15@正是那朵玫瑰老师有几个不明白的地方,误报和漏报不太明白: 1、假设A引用开始指向A1对象:A------>A1,按老师说的误报就是将引用A指向null:A------>null,那么此时A1对象不是没有引用了,不就可以垃圾回收了么,为什么会错过垃圾回收的机会呢? 2、漏报,是将A引用指向一个未被访问的对象假设对象为B:A----->B,此时A引用原来指向的对象应该没有引用了吧,为什么会垃圾回收器可能会回收事实上仍被引用的对象呢? 2018-08-15 作者回复 这里指的是,GC已经标记完成,然后其他线程进行修改的情况(也是并发GC所要解决的问题)。 当GC标记完成,还未开始回收时,你更新了其中一个引用,使之指向null,那么原来指向的对象本可以被回收的。 如果指向一个新的对象,这个对象可没有被标记为不能回收,垃圾回收器就直接给回收掉了 老师我也有和@正是那朵玫瑰一样的问题,看了老师的讲解,还是不太明白。GC标记完成,那GC标记的是引用还是具体的堆空间对象。如果标记的具体的堆空间对象,并不会造成GC并发问题,误报和漏报,改变的是引用关系。请老师解答,谢谢!展开共 2 条评论5
- same old love2019-06-17我有个疑问,就是JVM回收掉对象以后,存活下来对象的内存地址值会不会改变共 1 条评论4
- 茶底2018-08-15老师下一期能讲一下g1算法吗。讲深一点😁4
- life is short, e...2018-10-16老师,我心中有一个疑惑。 压缩算法是不是也用到了复制呢?因为我觉得在压缩的过程中,也需要把存活的内存进行转移,而转移也就是复制吧? 麻烦老师给回答一下~
作者回复: 确实是需要复制数据,这样起名主要是为了区分复制到同一个区域中(需要复杂的算法保证引用能够正确更新),还是复制到另一个区域中(可以复制完后统一更新引用)。
3 - Jerry银银2020-01-04老师,以下这段话是不是有误? 误报和漏报反了! 漏报应该是没有什么伤害,会让GC损失部分垃圾回收的机会。误报才比较麻烦,可能回收仍被引用的对象!! “误报并没有什么伤害,Java 虚拟机至多损失了部分垃圾回收的机会。漏报则比较麻烦,因为垃圾回收器可能回收事实上仍被引用的对象内存。一旦从原引用访问已经被回收了的对象,则很有可能会直接导致 Java 虚拟机崩溃。”展开
作者回复: 误报是指可达性分析误认为某个对象是可达的,实际不可达。漏报是指可达性分析没有发现某个对象可达。 这里需要明确,可达性分析会找出非垃圾,而其他对象通通被认为是垃圾。
共 4 条评论1