00:00
行,那大家讲清楚了,咱们说的这个对象的实例化的具体的这个步骤之后啊,咱们来看下一个问题,这呢称为叫对象的一个内存布局啊,就是说我们真正你有个对象咱都知道呢,都是放在这个堆空间当中的,对吧?那么我们在堆空间当中,你有的这个对象,它里边到底都有哪几部分结构啊,这呢就是咱们这个,哎这一节呢,想要解决这个问题,那我这呢也是画了一个这个思维导图啊,在PPT这块看也行,或者我们直接呢看,哎这里边的也可以,整体上来看的话呢,我们这个对象的在堆空间的内存布局当中有三部分,一个呢叫对象头,叫header啊,一个呢叫做实例数据instant data,还有一个呢叫堆积填充,叫pending。啊,这呢,就是我们所谓的这三部分,大家呢,先把这三部分呢记一下,那么提到这个对象头啊,咱们在前面讲这个对象实例化这个过程当中啊,其实也提到过这个对象头,对吧?就是在我们这个第五个步骤的时候呢,我们需要设置这个对象的一个头,诶对象头的这个数据,那这些对对象头这个数据啊,也是咱们面试当中,大家看到我这放在美团和蚂蚁金服里边,是不是都问到了一个问题了,说这个对头信息里边都有什么东西啊,对象头里边有什么,实际上是一个意思。
01:08
行,那这块我们就来看一下这个对象图里边啊,它到底存的是哪些东西。嗯,这个我们找到这个PPT。哎,来这看吧,那对象图里边啊,我们说主要包括了是两部分的内容,这个大家注意,一个呢叫做运行时原数据,一个呢叫做类型指针。嗯,这个这个运行时原数据和类型指针,那运行值原数据里边放什么呀?那咱们都知道咱们new的这个对象呢,在真空间当中,是不是它至少应该有一个手提指值对吧?你这个所谓的地值呢,是不是如果我们在一个方法中呢,去用的对象,你要给到我们这个引用变量,这个引用变量呢,在占空间的这个局无变量表里边,是不是它要指向你这个对空间中你用的这个对象实体,你得给我一个地址啊,对吧?诶这就是我们这个哈希马呢所起的这样一个作用,然后的话呢,我们前面讲过,说你对空间中这个对象放在这个伊甸园区的啊,它呢要呃回头呢,垃圾回收,如果没回收的话呢,放在这个survivor区,Survivor区呢,来回在进行GC的时候呢,如果它没有进行回收,它呢,有一个是不是叫年龄计数器啊,我们说叫age是吧,这就我们所说这个JC分带的一个年龄啊,包括呢,像我们还涉及叫这个锁状态的标志,比如说我们同步的时候呢,它是不是一个锁呀,这个我们需要给它做个标识啊等等诶这样的一些信息,像这些信息啊,我们都是放在这个运行时原数据当中。
02:27
那除了这个之外呢,我们还有一个叫做类型指针,类型指针就是这个指针啊,就指向了我们这个圆空间当中,或者你叫方法区也行啊,这个对象呢,所属的具体类型。啊,这个大家还记不记得,咱们在这个嗯,对象当中,比如我这呢,是一个对象,这个对象呢,咱们有个方法啊,叫做get class是吧,就是咱们这个object当中有这样一个方法,通过这个变量啊,咱们直接可以找到它是由哪个类呢所创建的get class其实呢,就相当于我们每个对象他都知道呢,它所属的类是谁,就是在我们这呢,有一个叫做类型指针。
03:01
啊,那这里边多说一句,就是并不是所有的这个对象呢,它都会保留这个类型指针啊,这要注意一下,那另外一个点呢,就是说如果我们创建的是一个数组,其实数组的话呢,我们说也也是一个对象对吧,那如果我们创建的是数组的话呢,还需要呢,去记录一下这个数组的一个长度啊,这个大家额外需要注意一下啊就可以了,行这呢,就是刚才我们看到美团和这个蚂蚁金服当中问的这个问题,就是对象图里边主要就是让你答了一个运营时原数据和我们这个类型指针的OK啊,那另外一个部分呢,就叫做实例数据。啊,实例数据不用多说,就是咱们用的这个对象,你这个堆空间这个实体里边肯定有当前自己这个类的,是不是定义的一些成现变量啊,对吧?啊另外我们说呢,也不管有你自己的,还有你从父类呢,继承下来的啊都有那父类的,父类呢,也同样道理,都算是你的负类,对吧?这呢,把这些数据呢,都放在我们这个叫诶实例数据这块。那这里边有个规则啊,就是规则什么呢?就是我们是先放负类中定义的变量,然后呢,再放这个此类定义的,那负类的负类呢,那肯定是更先放,对吧?这就是咱们以前讲这个Java代码大家都写过啊,就是我们在用一个对象的时候呢,是不是首先呢,要加载它的副类是吧?还在加载,呃,在加载它父类之前呢,先加载它父类的负类,Ug呢,我们最先加载呢,其实是object类,然后呢,再依次往下呢去加载,所以呢,既然呢是先加载这个负类,那继而的话呢,我们这个负类中的这个变量啊,一定也是出现在词类之前的,这个很好理解对吧?行,那么如果是同级别的话呢,我们说相同宽度的字段啊,它会分配到一起。
04:31
比如说你都是四个字节的是吧,哎,都是八个字节的,呃,相同宽度的呢,它会放一起,然后这块还涉及到一个叫comp fuse这样的一个参数啊说这个参数呢,默认值是个true,如果你要是true的话呢,这个子类的这个窄变量可以放到这个负类的这个诶变量的一个间隙啊,就是可放到他们这个空间,这样呢就是呃能够节省我们这个空间,这呢就是所谓的叫实例数据啊,然后最后这个呢,叫做对齐填充,这呢,其实大家呃了解一下就可以了,呃了解一下就可以了啊它呢没有什么特殊的含义,就是起到一个占位服的作用啊,就好比是大家这个咱们有一个快递一样啊,比如说你这是一个包裹,这个包裹里边实际放的是这个东西,比如是个花瓶很容易碎是吧,那我们在我们在这个花瓶跟这个箱子中间是不是会放一些这个泡沫。
05:18
那这个防止我们这个箱子碎,这个这个花瓶呢,在邮递的过程当中碎掉,那中间呢,你放的这个泡沫呢,就类似于咱们这个叫堆砌填充,所以这个呢,大家了解一下就可以。行,那后边这个图示啊,其实是,呃,网上看到一个图示啊,这个画的呢,非常一般一般,那我觉得不太有利于大家呢,去了解这个细节啊,一言不合呢,我自己呢又画了个图。行,那么通过我画这个图啊,咱们把前面涉及到的一个知识点,咱们再给它合在一起,大家看一看。好,大家看一看,我这呢对应的是这个程序啊,咱们刚才呢,这块不是涉及到一个叫customer这样一个类嘛,这个类里边呢,我生明了三个属性,其中一个属性呢,是account和account这块呢,还是这个另外的一个类啊,自定一个类行,那这里边呢,涉及到我们相应的一些属性,它的一些赋值,然后呢,我这里边写了一个最简单的就是用了一个customer。
06:09
好,我们来看一下这一行代码,从代码的层面来看的话呢,非常简洁,那这一行代码下去之后呢,我们内存空间当中是什么样的一个场景?对吧,什么样的场景,首先的话呢,针对于我们当前这个类来讲,我们这儿呢,没方法执行的线程呢,咱们就看作是叫主线程,那么在这个主线程当中,诶,我们主要考虑这个主线程,这个虚拟站里边,我们放了一个没方法对应的一个战帧啊,一个方法对应的是一个战帧,对吧?那么这个战帧里边呢,我们说就有这样的好几部分结构,局部变量表,操作数站,动态链接方法法规地址,附加信息,当然我们说最重要的是局部变量表和操作数站对吧?好,那么这个局部变量表对于我们这个may方法来讲,对于咱们这个may方法来讲,那一方面呢,它是一个静态的是吧?诶所以呢,它这个局部变量表的话呢,我们一开始就不用放这次了啊,非静态的采访,这呢咱这呢,直接第一个位置就放到X,哎,第二个位置呢,放在就我们这个cast。
07:04
啊,这个大家呢,如果说诶整不清楚,你也可以呢,把我们当前这个程序呢,先做一个编译。你编译完以后的话呢,这块也是看一下这个接class lab啊打开了是吧,打开以后的话呢,你就看一下我们这个酶方法,酶方案里边找我们这个叫,嗯,咱们应该找这个叫局无变量表了,是吧?在这个局部变量表里边,这就看到这个,诶就有两个值,一个呢是X,一个是Costa。哎,这是局部变量表,所以呢,在我们这里边是不是就是这个局部变量表,我这是用的虚线啊,就本身来说,其实应该写到这里边了,但是这有点太挤了,那我就放在这了,一个呢叫X,一个呢叫做cost。诶很清楚对吧,然后呢,我们这个,呃,以前呢,咱们讲课的时候呢,可能直接就说放到这个虚拟站里边了哈,那准确的说呢是呃,虚拟站里边有战争,战争里边的话呢,有具体局部变量表,局部变量表里边有这个叫cost啊,这个注意那这个cast呢,就指向我们这个对空间中你用的这个试体,咱们呢讲的这个对象的内存布局,主要呢,就说这块的,刚才说了,整体上来看呢,分成对象头实例数据和这个对齐填充它呢就是充数了是吧?诶这个咱们就不管了,那么这个对象头里边呢,主要有叫运行时原数据和类型指针啊这呢是有一系列的数据,咱们刚才提到了哈气值啊,这这个涉及到这个分带的时候呢,我们这个年龄计数器啊,诶,我们同步的时候呢,它要作为锁的话呢,它的一个锁状态的一个标志啊是吧,等等这样的一些信息,这呢叫运行时间数据,那此外的话呢,一个叫类型指针。
08:30
啊,这个类型指针的话呢,就能够指向你当前用的这个对象是哪个类的啊,那这个类的原数据的话呢,原信息的话呢,肯定是在我们所谓的这个方法区啊,那这块你说呢,它是一个叫这叫什么,呃圆空间也可以对吧,那就针对咱们JAVA8及以后的这个版本来说的。OK啊行,这呢就是指向我们这个真正你这个对象所属的类啊,然后的话呢,在我们这个实例数据这一块,就是我们这个层次的部分,诶那涉及到呢,它负类的实例数据的一个,呃,加载也要放在这儿,这呢我就略了啊,那针对于咱们当前这个对象来讲呢,它有一个ID,有一个name有一个啊actct是吧,Account这样的一个变量。
09:10
啊,这样变量,然后呢,这几个这个变量的一个赋值,呃,这个过程呢,其实涉及到咱们上面讲的这个情况是吧,咱们在前一节当中讲这个具体实例化的时候啊,看这也可以啊。哎,在这个具体实例化的时候呢,涉及到过我们这个叫诶默认的一个赋值啊,也涉及到过,涉及到过我们这个叫显示的一个赋值,对吧,那最终效果呢,就是我们这个对象呢,一旦创建完成以后,那我们呢,这个变量呢,就会有对应的这个值,那在我们这个程序当中呢,这个ID就是1001这个name的话呢,就是叫一名客户,那我们这个act呢,哎,就是还new了一个account,他们呢都被啊相当于显示初始化了。行啊,都被显示初始化了,都没有用,我们一开始的默认值啊,默认值呢,它是零,它是no,它也是no是吧?行,那么回过来的话呢,对应的我们这个ID呢,这就是1001,然后这个name的话呢,我们是给它附了一个字符串常量。
10:03
那这个字符串常量的话呢,我们说在JDK7以后,是不是就放到我们这个叫对空间了是吧?字符串常量池里边有一个叫匿名客户,诶这样的一个字符串信息,诶就通过我们的name呢,就指过来了,哎,这是这个事儿,然后呢,这个act呢,就是我们这里边的一个还是一个成员变量啊,它呢,因为你用了个对象嘛,所以这就我们用的account这个对象啊,这呢也是一个内存布局,这个展开以后呢,跟它又是一样的,所以我就省略了啊,然后在我们这个account这个对象实例当中,它呢也维护了一个类型指针。指向了我们这个方法区里边这个account的一个流源数据行,那我这个图呢,画完以后大家应该就非常的清晰了。OK,那这呢,咱们就把这个对象的一个内存布局呢,诶就说清楚了啊,那这个说清楚以后,大家你再去面试,你是不是都可以编一些问题呢?哎,问这个面试官了,哎,他也不一定能记得多清楚是吧?哎,这里边儿应该说是非常清晰啊,OK。
我来说两句