文章目录
对象的创建
当字节码执行到了new指令,会自动到常量池中找的该变量的符号引用,从而找 到该类的直接引用,判断该类有无加载解析初始化,要是没有就会先执行加载过程。然后就会在堆内存开辟一个空间存储。
若堆的内存是排列紧密的(Serial、ParNew的标记整理法),被使用的内存放在一侧,没有被使用的内存放在另一侧,那就只需要移动使用和被使用中间的指针,移动新创建变量大小的距离就可以了,这种方式也叫指针碰撞法。
若堆的内存排列不是紧密的(cms的标记清除法),就会创建一个列表,上边记录着内存具体什么位置有多大内存的空间,找到可以装下对象的内存,分配好后,只需要在表上记录就可以了。
内存分配时会遇到多线程的情况,可能第一个线程在该内存上分配还未分配完毕,之后的线程同时也在该内存上分配,就把第一个线程的内容覆盖掉了,为了阻止这样的情况发生有两种方法
第一种是cas加失败重试方法。第二种是在堆上为每一个线程都划分一个私有的空间,现在空间里划分内存,不够再到堆中划分内存进行同步锁定。
内存划分完毕,初始化内存(给内存中的变量都复制为0),开始设置对象头的信息,最后再执行字节码的init方法(给内存中的变量复制具体的值),结束。
对象的内存布局
分为三部分,对象头,实例信息,对齐填充
对象头:存储两种信息,第一种是运行时数据,存储的内容有哈希码,gc年龄。存储空间在32位的机子里空间是32比特,64位的机子里空间是64比特,因为数据太多盛不下,所以就用空间复用的方式来存储。第二种是指向类变量的指针,指针不是必须要有的,也可通过其他方式也可以知道类变量,通过类变量可以知道对象实例的大小,但是对于数组来说,一定要存储自身大小的信息,数组无法通过类变量推出。
实例信息:存储对象中的各种类型字段内容,还有从父类继承的字段。
对齐填充:对象的内存是8比特的整数倍,不为整数倍的通过对齐填充补齐。
对象访问定位
句柄池、指针两种方式
句柄池方式:局部变量表的reference指针指向堆区的句柄池,池子中有两个指针一个指向了堆区对象实例,一个指向了方法区的类变量。
优点:当对象信息改变时只需要移动从句柄池到对象实例和到类变量的指针即可
缺点:比指针方式多了一次访问(先到句柄池在到实例对象),访问速度慢
指针方式:局部变量表的reference指针直接指向了堆区的实例变量,堆区的实例变量指针指向了方法区的类变量。
缺点:当对象信息改变所有指针都要变化。
优点:比句柄池少了一次访问,访问速度快,Hotspot虚拟机采用这种方式。