00:00
那下边啊,我们来说一下这个过程五叫做类的卸载,前面的话呢,我们讲了这样的几个过程啊,像过程123对应的叫做加载链接和初始化啊,这呢就好比是大家呢去买菜洗菜这个做菜这样的一个环节,对吧,那做完菜之后呢,这个菜呢,就可以摆到桌子上了,那你做菜的目的呢,肯定是为了吃饭嘛,对吧,那吃饭这个过程呢,就对应着我们叫类的一个使用。那对于我们平时开发来讲的话呢,咱们主要集中在就是类的使用这个环节,所以我们针对这块呢,就没有细节细节呢去展开讲,或者说呢,这块呢,呃,也没有具体可以细节展开的讲的这个事儿了啊,那就是我们造完对象之后,你去调用丰富的你这个类中生命的那些方法结构,对吧?啊OK,那针对于我们平时开发呀,咱们较少的去关注于过程123,所以这里边儿呢,咱们针对于过程123,也是咱们重点讲解的这个环节。好,那么你吃完饭之后呢,是不是最后还得要把这个碗筷呢,收拾一下,刷一刷对吧,那这呢,就好比是我们这个类呢,还需要进行一个卸载,那我们知道这个类呢,咱们存储在这个方法区当中,那如果说呢,你这个类不及时的进行卸载,或者说呢,我们没有进行过卸载,就有可能会出现OM。
01:13
对吧,哎,我说的是有可能,因为呢,我们JDK8之后呢,方法区主要的落地实现呢,叫做圆空间,那原空间呢,使用的是我们这个系统内存。那那这个系统内存呢,我们说相对来说会更大一些,对吧,那一般的时候呢,我们说都不用方法去呢,去进行这个JC,那这个即使JC的话呢,这个频率也非常的低,对吧。那关于我们这个类的卸载这块呢,具体细节有哪些呢?咱们展开呢,来说明一下。那要想说清楚这个类的卸载,首先的话呢,我们需要捋清楚三个结构之间的一个关系。这三个结构呢,分别对应的就叫做类啊,类的加载器啊,以及呢叫类的实例。好,那比如说我们现在有一个类啊,叫做sample啊,叫做这样一个类,这个类的话呢,我们把它加载到内存中,咱们说呢,就存放在这个方法区,我们称呢叫类的啊模板结构啊,或者呢叫二进制的一个数据结构啊,就存放在这儿,那对应的我们在会在这个对空间当中啊,生成一个是不是大的class的一个实例啊,啊大class这个实例呢,就指向了我们方法区中你这个类模板的这个结构。
02:18
好,这是我们说的第一个结构,然后第二个的话呢,我们提到说任何一个类啊,咱们要想加载到内存中,都需要使用它对应的叫类的加载器。哎,类的加载器,所以呢,我们需要关心一下类和类的加载器的一个关系,以及呢,我们还可能会去创建当前你这个类的这个实例啊,Sample的这个对象,对吧,那对象呢,跟我们这个class的实例之间是什么关系呢?这呢也是我们要描述的。好,那把这三者之间的关系说清楚之后,咱们才可以去讲清楚这个类的一个卸载。行,那首先呢,我们来看一下这个类和类的加载器之间的一个关系,哎,它两者之间一个关系说呢,在类加载器的这个内部实现当中,类加载器啊,说用一个Java集合来存放所加载的类的引用。
03:07
说白了就是我们这个擂台加载器呢,它也是个对象啊,分节对象呢,诶我都加载了哪些类呢,我用一个集合呢,来进行一个存储。那这呢,就涉及到我们提到这个类的加载器,咱们在讲这个上篇的时候呢,那其实带着大家呢,也讲讲过这个事儿,对吧,咱们下一章呢,还给大家再提一下这个类的加载器的问题。那主要的这个类的加载器呢,在咱们这个GM当中提到有这样几个,首先呢叫bootrap呢,Class,我们称为呢叫引导了接载器。或者呢,也叫做启动类加载器,第二类呢,叫做extension,叫做这个扩展类加载器。第三类呢叫application class称为呢叫应用程序类加压器,或者呢叫做系统的加压器,那像我们说Java的这个核心类库呢,都使用的bootrap了,那特定的一些炸包呢,用的是extension,然后我们自定义的一些类呢,使用的叫application,对吧?那首先明确我们这个类的加载器啊,造的一个具体的一个类的加载器对象,它不止加载一个类啊,像我们一个程序当中,咱们会写很多的自定义类,那这一类呢,都是由我们这个,哎,系统类默认情况下都是由这个系统类加载器呢进行加载的。
04:15
OK对吧,行,那我们一个类的加载器呢,它就有可能要加载多个类,那这时候呢,我就用一个集合呢,来进行一个封装。言外之意呢,就是我们这个通过一个类的加载器呢,咱们就能够,呃,找到你这个加载的对应的那个类是什么,所以我们这有一个指针指向啊,由这个class loader这个对象呢,就能够找到它加载了哪个类,哎这儿呢,就指过来了。那另外一方面呢,我们在这个class里边,咱们有个方法是不是叫get class啊。对吧,诶诶错了,是这个,呃,我们在这个大的class里边呢,有一个方法叫get class loader,我们通过这个大的class这个对象呢,就能够获取你的类加载器是谁。那YG呢,就是我们通过这个大的class对象呢,也可以找到是谁加载的它,那这时候呢,是不是就构成了一个双向的指人呢,那我们就称为呢,叫双向关联,你中有我啊,就是你关联了我,我也关联了你,那就是这样的一个意思。
05:12
啊,这就是相互引用对吧?然后接下来呢,我们再来看一下这个类的这个对象,我们现在呢,创建了一个sample类的一个对象,那我们通过这个对象呢,是不是可以调用object类中定义过的一个方法,叫做get class对吧?那get class呢,就能够获取你这个对象呢,是由哪个类创建的,那言呢,我们这是不是就有个这样的指针呢。对吧?哎,这个指针就存在行,那通过我们刚才这样一个描述呢,我们这个就刻画了说这样的几个指针的一个指向情况,那相对应的你是对空间中的几个对象,我们在站里边呢,就会有相应的这个引用变量,哎,去指向你堆空间中的这个对象,那否则的话呢,这个对象呢,就可能是一个垃圾了,是吧?哎,我们这呢,需要给它引用一下,那在我们这个占空间,它存储的这几个变量呢,哎,我们说都在局部变量表里边,那局部变量表呢,是不是也是我们在上篇讲过主要的这个JC入S啊,它的这个存放位置是吧?哎,根节点这个位置。
06:07
好,这呢,我们就说清楚这个关系些啊。这个关系呢,说清楚之后呢,下边我们来看一下叫类的一个生命周期。说呢,当你这个sample这个类呢,被加载链接初始化之后呢,生命周期就开始了,说当代表三这个类的这个class对象呢,不再被引用的时候,那就表示呢,你是不可触及的了,你不可触及了,你这个大的class对象呢,就要结束生命周期了,那它结束生命周期,那这个要结束了,这个大的呃,Sample这个类的二进制的这个数据结构呢,也可以呢,就要被卸载了,所以生命周期呢就结束了。啊,这个说的比较简单,那下边呢,哎,我们得展开说一下这个具体的细节啊,细节是什么情况,你看刚才提到了,说一个类呢,到底何时结束生命周期,那我们这块呢,主要是看你这个大的class的这个对象何时结束生命周期,对吧?那只有呢,你结束了生命周期之后,是不是这个指针就不存在了,这个指针不存在了,我们方法其中你这个类模板的这个结构是不是才有可能是不是会被回收掉啊。
07:07
那关键的就是我们看一下这个大的class对象什么时候才能被回收呢?大家想想,我们这个图其实画的很清楚。嗯,大家你看啊,我们要想这个对象呢,被回收掉属于堆空间是不是里边的一个对象呀,它要是被回收的话呢,首先你这个哎,占空间的局部变量表里边是不是这个指针得干掉啊。对吧,这个指针得干掉,那这个指针干掉呢,我们会发现,诶我这个指针指过来,指过来是不是还引用了,所以它呢就不会被回收,怎么办呀,你是不是这个指针也得干掉,这个指针干掉呢,就依赖于咱们呢,关于这个sample这个类的实例啊,都没有了。这个类的实例呢,都没有了,这个指针就没了,这个指针一没有的话呢,它呢,是不是就可以判定是一个垃圾的是吧?这要是垃圾的话呢,这个指针其实呢,它就诶可以不要了。这个指针就可以不要了,行,然后这是一方面,另外一方面的话呢,我们这个结构是不是你要是这个对应的class load这个指针还在,这个指针就指向它,它又指向它,相当于这个class对象呢,还被关联到,那仍然不能被回收,那怎么办呀,是不是得让它对应的这个指针不能有啊,这个针要不能有,我们是得把这个指针给它断掉啊,这个指针一断掉,这个class load的这个实力呢,就没有了,它没有了,这个指针就没有了。
08:22
那这时候呢,诶就断掉了,断掉以后它呢,诶就被看成是一个不没有任何引用可以指向的这样一个结构了,啊就不可达了啊re unreable对吧?它不可达的话呢,我们要回收了,把它回收掉,那对应的这个指针是不是才可以消失,它消失了这个是不是才可以回收。大家听我描述的这个过程,是不是感觉就很恶心是吧?哎,所以这里边儿呢,你看要想我们这个类模板,这个结构被回收,大家会发现呢,这个是不是非常的苛刻呀。对吧,哎,非常的苛刻,行,那这块呢,咱们首先来回顾一下,咱们在讲这个上篇的时候提到过关于方法区的这个垃圾收集的问题,那方法区这个垃圾呢,主要是常量池当中废弃的常量和不再使用的类型。
09:10
那关于这个废气的这个常量的判断啊,其实比较简单,就是你不再使用了,那我们这块呢,就诶直接呢给你回收掉就完了,那关键呢,就是我们判定这个类类型不再被使用了,它就比较苛刻,需要同时满足下述三个条件,那当然你看一下我这里边,哎,写的这三个条件呢,是不是跟我们这儿呢,恰好是一一对应的啊,你看过来一下,怎么叫三个条件呢,说该类所有的实例呢,都已经被回收了。那是不是就涉及到这些对象,是不是全部都没有了?那其实呢,就意味着堆空间中不存在该类及其任何派生子类的实例。因为呢,我们说你这个具体的此类的对象是不是也可以调用负零的结构了,所以你此类对象也不能用啊,另外一个呢,就是加载该类的类的加载器已经被回收了,就是说它呢啊,它呢需要被回收,这个事儿的话呢,你看这块写的,除非呢你是经过精心设计的,否则的话呢,这个类的加载器通常不会被回收。
10:06
啊,这个事儿很难被达成,那这个很难被达成,其实就导致我们这个类模板结构呢,其实很难被回收啊,这个呢,咱们等一下再接着描述啊,大家先知道这个事儿啊。就是他呢也得干掉,那以及呢,就是对该对对应的这个该类对应的这个大的class的这个对象呢,也没有任何地方呢去引用啊,相当于这个呢也得干掉啊,主要呢,就是上下干掉之后,它才有可能被干掉,对吧,因为这块这个指针也不能存在,那这块都干掉了,我们才保证这个指针就没有了,它才可能被干掉。啊,你看下边这块就写的说满足以上三个条件呢,我们只能说呢,是允许被这个卸载了,但是呢,那我们不一定说必然会被回收。啊呃,这个还有一个原因是因为咱方法区的话呢,通常这个空间呢,我们进行这些的这个频率本身也很低是吧,通常一般呢,关于这个类型这块呢,通常你回收一下之后,发现这个效果呢,非常的不明显,因为呢,我们中间这个事儿呢,其实很难达成。
11:05
哎,中间这个事呢,很难达成,那怎么去理解这个事儿呢,大家你看一下我这里边儿写的这个类的卸载。啊,主要呢,我们这块就提到这个关于类的加载器的问题,好首先呢,我们提一下叫启动类加载器,或者呢叫做引导力加载器,就是不trap呢class order这个bootra classo呢,它加载的我们说是整个运行期间核心的一些API。像object啊等等这些都是属于核心的API,对吧,那么这些核心的API呢,注意咱们在整个运营期间呢,是不可以被卸载的。这个类不能被卸载,那你你相应的你这个启动类加载器呢,其实也不能够被卸载。啊,因为咱们扎瓦虚尼呢,本身它始终呢,会去引用这些驱动类加载器。啊,会使用这个启加器,进而的话呢,你这个核心API呢,本身也不能够被卸载掉,对吧?哎,这个大家去理解,那GM呢和GLS这个规范当中其实都提到这个事儿了,言外D呢,就是行为加载器加载的这些类啊,大家你就别想了,在整个运行期间它卸载不了啊,那它呢,你就干掉了啊,别想了,然后接下来的话呢,你再看一下这个所谓的引导力加压器。
12:12
哎,那看下这个引导类加载器游戏吗?包括呢,我们下边这个自定义的这一类,我们还可以使用叫系统类加载器,对吧,那被系统类加载器或者叫,诶刚才说说错了,不是引导类这个扩展类是吧?这个引导类还是咱们boop啊,这个扩展类加载器和我们的系统类加载器他们呢,对应的加载这个类型在运行期间呢,也不太可能会被卸载。因为呢,系统类下载器这个实例,或者是这个扩展类的这个实例呢,基本上在整个运营期间总能够直接或间接的访问得到啊,所以让他呢,达到这个unreachable的这个可能性啊也极小。你想想我们这个创建了一个系统类加载器的一个对象,这个对象的话呢,我们加载了很多的,比如说自定义类,那你可能这两类呢,现在不用了,但是我们这一类是不是还用了,那要这一类还用的话呢,我这个性用类下去是不是就不可能被卸载呀,他要不能被卸载,那你现应的这个类呢,他们就有可能会被指向嘛,那指向的话呢,他们想卸载这个可能性也不大呀,虽然说你不用了,但是不能被卸载,被引用着呢。
13:14
是这意思吧?好下一个那被开发者自定义的这个类加载器,那下边就归结成我们要想说去卸载一个类啊,咱们现在呢,中心就归结到咱们自定义类加载器了,那你自定义的类加载器呢,咱们去加载一些实例,那你到时候可以考虑呢,去进行类的一个卸载。啊,而且呢,这时候你得保证在比较简单的上下文环境当中才能够被卸载。啊,而且一般呢,还需要借助于,借助于这个叫强制,呃,借助于强制调用虚拟机的垃圾回收器,才能够实现一个回收。那你像在这种稍微复杂的一些场景当中,比如说呢,很多时候呢,用户开发自定义类加载器实现实例的时候呢,咱们采用这个缓存的策略,那我们不需要呢,重复的去加载,那这时候这个系统性能会提高,那你要采用缓存的话呢,我们就不可能去把我们这个自定义类加载器呢,就进行一个卸载,它不卸载的话呢,你对应关联的那些加载的类呢,也不会进行卸载。
14:09
啊,所以总结一句话呢,就是我们呢,其实对于这个类型的一个加载之后,想给它卸载这个事儿呢,其实从概率上来讲,几乎是不太可能啊来进行的。那或者至少来说呢,我们被卸载的时间呢,是非常不确定的啊,你要都满足的话呢,还得看垃圾收集器什么时候去做执行了,对吧?啊OK,那所以说大家呢,再去这个思考,我们这个程序设计的时候呢,你不要建立在我们这个类呢,要被卸载的情况下呢,你再去实现特定的一些功能啊,这是不现实的,因为通常情况下,我们这个类的卸载呢,都很难进行。OK,那通过我刚才这样的描述呢,大家需要掌握几个事儿,第一个呢,就是这个结构图,那你最好你会画,那凡是对这个知识的理解,你要能够以图形的方式呢去呈现,那就说明你的理解呢是比较好的,哎,这是这个图,然后就是理解我们这三者之间的关系,那另外一个的话呢,就是大家需要清楚,像启动类加载器,系统类加载器和扩热类加载器,它们三者加载的那些类,包括它们三者之间,哎,它们。
15:12
基本上在整个运营期间是不可能这个被回收了,那只有呢,我们自定义的类加载器,以及自定义类使用这个类加载器,但使用你这个自定义类加载器的这些场景才有可能我们被回收。啊哎,这呢,就是我们总结的这个结论啊行,那至此的话呢,咱们关于这个类的加载过程啊,涉及到类的生命周期呢,咱们就讲解到这儿。啊,讲解到这儿,那重点呢,大家关注一下,就是关于初始化,还有关于我们前面讲的链接里边这三个环节这块。行,那这一章呢,我们就到这儿。
我来说两句