00:00
诶,那接下来啊,咱们就来家讲解这个关于叫垃圾标记阶段这个算法,哎,首先呢,咱们来给大家讲解的叫引用技术算法,那另外一个算法呢,叫做可达性分析算法,那我们也要关注一下这两种算法呢,具体的实现原理以及呢,它们二者有什么区别啊,咱们先一个一个的来讲解。好,那我们来看这个PPT,那这块呢,你看我写说垃圾标记阶段,说对象是否存活,诶这个怎么来理解这个事儿呢?诶我们一旦呢,提到说垃圾的一个标记,或者说呢,这是一个垃圾,那首先呢,咱们需要明确说哪些地方存在着这个垃圾,然后需要我们去进行这个JC。对吧,哎,那这块呢,我们就需要呢,回顾这样一个图,首先呢,咱们来看这个结构。这个咱们前面呢,其实已经反复的给大家讲过几次了啊呃,在咱们这个内存当中啊,有这样的几个部分,哎,方法区一堆啊清接收器,本地方法站和虚拟基站,那么说存在这个JC行为的呢,其实就是方法去和对空间。这是两个存在着这个JC,不光存在JC,他们呢,是不是也存在着这个叫oom啊,啊叫内存溢出行为,对吧,那对于这个程序计数器来讲的话呢,我们说它既没有GC,也没有是不是相关的这个内存溢出的行为。
01:13
它是什么都没有对吧,然后对于这个本地方法站和虚拟基站来讲呢,说这俩兄弟呢,是没有JC他俩呢不存在垃圾回收,但是呢,他们是不是存在的叫stack overflow啊,他们有这个内存溢出的行为啊,这块大家要注意一下,咱们前面呢已经说到过几次,OK,那咱们现在呢,叫垃圾回收,那肯定是不是针对的就是有这些的这个场景,那也就是说我们说的方法区和对空间,OK,先明确一下这个问题,那其次的话呢,我们再要强调一个点,就是说这个方法区,咱们当时呢,看这个Java虚拟规范的时候,大家还记不记得,当时咱们这个带着大家翻译过一段话,说呢,这个具体的啊,Java虚拟拟规范的这个实现啊,那也就是所谓的具体垃圾回收器了是吧?哎,具体这个GM了说呢,我们并不要求说具体的GM1定要去回收方法区的。
02:03
哎,大家回忆一下,咱们当时说过这句话啊,也就是说呢,咱们这个方法区的这个垃圾回收,不不不具有这种普遍适用性啊,很多这个垃圾回收器里边这个在扎瓦虚拟里边是没有这个。回收方法区的行为的。啊,是有这样的这个GM的啊,嗯,行,或者说呢,具体的就是针对的就是具体的这个GC是吧,垃圾回收器来讲啊,那这是一个事儿,另外一个呢,就是咱们前面讲这个堆的时候呢,也提到过一个点说呢,咱们会比较频繁的,是不是收集这个叫新生贷较少呢,收集这个养老贷说基本不动这个永久贷,那所谓的永久贷呢,就是我们方法区的具体实现了叫永久贷,那这个八零后呢,我们叫圆空间,也就是说呢,基本上我们会不动这个,呃,方法区的更多的行为场景,我们都是针对这个堆空间来讲的,所以呢,哎,经过这样的一个层层分析呢,我们首先明确一下,咱们现在这个JC主要针对的就是堆。OK对吧,那既然呢说的是堆了,那么我们就要考虑一下,这个堆里边主要是放什么呢?我们说堆里边主要放的是不是就是对象了,哎,那这个时候呢,我们就确定好了,说咱们呢进行垃圾回收标记,标记的是什么呀?标记的就是这个对象哪些呢该被回收,哪些不该被回收。
03:15
哎,就是这个道理啊行,那这块看到了说堆里边啊,几乎存放着所有的障碍对象啊,这地回收之前呢,我们首先判断呢,说哪些是活的,哪些已经死了,把这个死呢,咱们进行回收,把它这个空间呢释放掉,然后呢供新的对象呢占用啊咱这呢就称为叫标语阶段。啊,这个是吧,那既然成为这个叫标记阶段了,我们需要明确一个问题,就是标记,那怎么算叫一个对象已经死了呢?怎么算死亡了呢?这话说,简单来讲说,当一个对象已经不再被任何存活的对象去引用的时候。那他就宣告死亡了。对吧,这个能理解吧,比如我们这呢是一个占空间,这呢是一个对空间,对空间里边我们有个对象,然后在咱们站里边有个引用,这个引用呢,就指向你这个对象了,那指向你这个对象呢,这个对象呢,就有可能是不是通过我们的引用呢进行调用了,那这时候呢,你就哎不能死,那如果说呢,现在存在另外一个对象,说这个对象呢,没有任何的指针指向你了,你呢我跟说啊就是一个垃圾,那我们呢,就要把你回收掉。
04:14
简单来说就是这个道理。那其实这个呢也很好理解,大家想想这个人是不是也一样啊,针对咱们人来讲,你说这个人的这个价值是是什么呀,怎么体现这个价值大或者价值小啊,哎,我们说其实就体现了你这个人被需要的一个程度。啊,我们说历史上是不是有很多那个伟人是吧?啊把国家给统一了,当然咱们国家也是一样是吧,毛主席,哎,把这个。这个这个分割,这个分裂了这么多年的这个中国,然后给统一了啊,建立了新中国,大家过上好日子了,那我们就出卖毛主席是吧,哎,那就相当于他呢,就呃跟其给给给所有的中国人民呢,带来一个幸福的生活是吧,他的价值呢,就非常的大,那如果说这个人的话呢,有他跟没他呢,这个没什么区别,那这个人的价值呢,显然呢,他就比较小一些。
05:00
哎,大家能理解这个概念对吧?啊一说到这儿呢,我想起来这个以前看的一个故事啊,哎,当然呢,第一次这个比较早的时候看到的啊,感觉还挺有道理的啊说这呢是一个病房里边的两个病人啊,这个年轻力壮的病人,但是呢,生病了啊,得的是一种很奇怪的一种病是吧,这俩呢病病是一样的,然后这个症状呢,也比较接近,那其中一个这个男人的话呢,这个家里比较穷啊,媳妇儿呢,就是天天来这儿哭诉是吧,看看护他的时候哭诉说呢,这个你可不能死是吧,上有老下有小啊,说家里边都指望着你呢,你这是唯一的劳动力啊,这个你一定要好好活着是吧?啊就每天呢,给他埋怨抱很多的这个家里边的一些问题啊,这个这是一种场景,那另外一个这个男人的话呢,你两个人年龄呢都比较接近啊,另外一个呢,就是家里边比较富足一些啊,也比较有钱啊,这个呢,通常媳妇过来后说说你不用担心是吧,好好养病啊,这个家里边什么事呢,你也不用担心啊,都有人去看管着啊,你就好好养病就可以了,哎,天天这样的去宣传,结果呢,发现隔了一个月之后呢,这两个人。
06:00
这个病情啊,有了很大的变化啊,第一个就是比较穷人家这个人呢,后来呢,你发现他这个症状呢,越来越轻啊,以至于最后呢就直接出院了,而我们下边这个富人家的这个人的话,最后呢就哎不幸呢就死掉了。啊,因为呢,他觉得说哎生活中都不需要他了,那可能那他就挂了是吧,上面这个的话呢,生活中哎太需要他了,家里边没有他不行,所以呢,他这种求生的欲望啊就非常的强啊,最后的话呢,他他反而呢就活下来了。那针对于我们这块来讲的话呢,就是哎,你有被需要的一个价值啊,你有被引用的一个价值,那你呢就不能死,那下边这个呢,没有谁这个需要你了是吧?嗯,可以就挂掉了啊,他们俩就像是个垃圾一样了,哎通过这个呢,大家去体会一下啊,行,这呢就是咱们说的这个问题,那么关于判断这个对象存活,咱们说呢,有两种方式,一种呢叫引用基数,一种呢叫可达性分析,咱们现在呢,主要讲的就是叫应用技数,好我们来看一下这个英文呢,叫reference counting。啊,就是引用的一个计算它的个数比较简单是吧,说呢,对每一个对象咱们都保留一个整形的,叫引用计数器属性。
07:04
诶大家还记不记得,咱们前面讲了一个,哎叫新生代,新生代呢,这叫伊甸园区,这呢叫这个survivor区是吧?然后呢,在这个survivor区里边呢,我们也有一个age大记住了,说你这块儿,诶从这儿导到这儿几次了,导到这儿几次了,说达到那个预值15的时候呢,默认的时候就进入这个叫老年代,对吧?当然呢,我们也试的一个计数器的问题,那这呢是另外一个计数器,这个计数器呢,它主要干什么用呢?就是来记录对象呢被引用的一个情况。哎,就是如果说你这有个对象A啊,哎,你要是别的对象引用你了,那你这个计数器呢,就加个一,你要说呢,这个引用呢,失效了,我们就减个一。加个一加个一非常简单对吧?说呢,呃,如果说你这个对象A的引用计数器的值为零,那就意味着呢,没有任何的引用指向你了。没有银河任何引用指向你了,那就是零,那你既然是零了,就意味着你就是个垃圾,就可以被回收了。这个原理呢,非常简单啊,大家呢,如果面试问你说什么叫引用记录这个算法,你就把这个事儿呢说清楚就行,对吧,那优点是什么呀。
08:06
想想优点。啊这块呢,其实我都写出来了,对吧,实现呢比较简单啊垃圾对象呢,也便于标识,怎么来讲呢,你就看一下那个属性,你看这个属性的话呢,它是不是零,它要不是零了,那你那你就这个这个相当于不能回收,他要是零了,那你就得回收对吧,大家说诶这个这数能是负数吗。是不是不可能是负数,我们加一加一加一,然后你可能往往下减减减减到零的时候呢,就被回收了,不可能是负数啊,这个这个别蒙了,这就便于标识,直接看这个属性值就行,然后下边呢叫判定的效率比较高,说回收呢没有延迟性啊,这个能理解吗?哎,效率比较高。啊,这个呢,其实主要呢,针对于还是咱们下边要讲的叫可达性分析这个算法。诶,可达性分析这个算法的话呢,我们就涉及到叫GC roots是吧?哎,通过这些roots呢,在一个一个去找,所以呢,它相较于咱们这个引用技术算法来讲的话呢,这个引用技术算法呢,它的效率呢,确实是比较高的啊,咱们呢不需要等待这个内存啊,说白了就是不需要等待这个内存不够的时候。
09:08
我们进行垃圾回收。正常咱们说这个JC这个呃,垃圾这个内存不够了,我们进一个JC对吧,然后这时候呢,可能还需要填一下这个用户线程啊,而对于我们这个引用技术算法来讲呢,其实没必要,就只要你随时发现这个引用计数器是零了,你是不是随时可以回收啊,所以它呢是没有延迟性的。哎,注意这个问题。那我这优点呢,算是写了两点,那缺点这块呢,写了三个。啊切了三个,这三个里边呢,其实这个第三个呢是比较致命的,那前面两个呢,其实都还好啊看一下,那因为呢,你需要用一个引用计数器的一个属性嘛,所以呢,我们增加了空间上的开销啊,不过这呢也就是一个变量而已啊,开销呢其实不算大,相较于说它这个效率高来讲,这都是小case对吧?下一个说每次赋值的时候呢,都需要去更新这个计数器啊,伴随这个加法和减法操作,增加了时间的开销,这呢当然也是一个情况,上面呢就是从空间上来讲,下面呢就是从这个时间上来讲,一般呢,咱们说衡量一个算法,是不是就考虑这个叫空间复杂度和时间复杂度,对吧?当然更关心的我们是不是还看一下这个叫时间复杂度啊。
10:12
那这个时间法杂度这块呢,说的是加减有开销,但是相对于这个来讲,它肯定这个时间上考虑优点更明显一些。下一个说呢,引用计数器啊,它有一个严重的问题,就是说没法处理叫循环引用的一个场景,这呢是一条致命的缺陷导致呢,就是其实就是因为它无法去处理这个要循环引用的问题导致呢,咱们Java这个垃圾回收器呢,在使用的时候就根本就没有考虑这个叫应用技术去引用技术算法。所以咱们讲了半天呢,最终的结论就是说Java呢根本不用它,那你知道不用的原因呢,就是因为他无法去处理这个循环引用的问题,那什么叫循环引用,大家来看这个图。大家来看这个图,我们这儿呢,是一个引用,它指向了就是这个对象实体,那这个对象实体里边啊,它呢,还有一个这个呃,里边一个属性指向了另外一个对象,那这个呃,Next属性又指向这个对象,这个这这个对象呢,又有一个next属性又指向了这个对象,那我们里边呢,是不是就有这个叫reference counter啊我们相应的标志这个呢,是不是这是一个指向它的,这是一个指向它的是不是就二啊,然后呢,他们这个就是一,这个是一。
11:17
这是这个场景,接下来的话呢,我们这个P。这个P的话呢,我们指向是一个闹了,说白了就是我们这个指向这个这个别的地方了,或者说呢,我们这个就指针的就直接给干掉了,在干掉的情况下呢,我们再看这三个对象呢,它们各自的counter上面减过一个就全是一了。而事实上呢,这三个对象呢,可能我们就根本不再去使用了。这个呢,是咱们呃写程序当中比较根上的这样的一个引用变量,通过它呢,我们去连接这个next啊意思呢,诶连接这个对象,通过这个next又指向它一次呢去使用,那现在我们这个P的这个指针不要了,其实这几个对象了,按说也不想要了,但是大家注意发现呢,是不是他们的这个引用计数这个值都是一,所以导致呢,他们仨呢,就没有被哎JC回收掉。
12:03
哎,因为呢,没有被JC回收跳。啊,所以呢,我们说这呢,实际上也是一个内存的泄露行为。关于内存泄露呢,不知道大家呃,是不是特别清楚,咱们呢,后边呢,下一章啊,给大家也说一下内存泄露和内存溢出的这个行为,就是到底怎么叫内存泄露,大家要明白啊,这个内存泄露简单来说就是你这个本身这个对象啊,它其实呢没有用了,没有价值了,按说应该被回收,但是呢又回收不掉,那这呢就叫内存泄露,那大家来看是不是它们就是这样,这三个呢,不用了,但是呢,又因为他们的值不是零,所以还没法回收,所以呢,这叫内存泄露。啊,这样的内容,但是啊,我要说一个butt是吧,但是但是什么呀,大家如果在面试的时候,人家面试官问说你能不能举一个内存泄露的例子。就是Java当中是吧,大家要不要取这个内存泄漏呢。要不要举呢?啊,建议呢,就不要举了,或者你非要举的话呢,你一定要说清楚啊,你举的这个内存泄露的例子呢,属于叫引用技术算法里的内存泄露。
13:05
什么意思啊,就是因为咱们Java呢,是不是没有选择这种算法呀,那既然咱们没有选择这种算法,那就意味着在咱们Java成语当中,是不是说就不会出现这种内存泄露行为了。啊,那你这个举例子的时候,是不是你就不要举这个例子了,你说啊,我举个这样例子,举完以后呢,说诶张化中反而也不用,不用,那就意味着张号中不会出现这种内存泄露行为啊,所以大家呢,就尽量不要举这个例子,那那应该举什么例子呢?诶咱们下一章当中,咱们会给大家说这个内存泄露的这个情况的啊行,那首先的话呢,大家先从我们这个概念上啊,对于这个叫已用计数算法呢啊,应该有一个清晰的了解,优点是什么,缺点什么,它的实现原理是什么啊都要懂。
我来说两句