jvm 对象的创建 布局 访问

心情不是太好,本来想去健身,后来想想还是写博客吧!!

谈谈对象(普通对象不包含数组、Class对象)的创建:
虚拟机遇到new指令时,首先检查这个指令的参数是否能在方法区的常量池中定位到一个类的符号引用,并检查这个类的符号引用是否被加载、解析、初始化过,如果没有就先加载(稍后说类加载过程),在类加载检查通过后,jvm为新生对象分配内存。对象所需的内存大小在类加载完成后就能完全确定,。
为对象分配内存;根据jvm堆内存是否绝对规整(是否使用压缩GC算法):
绝对规整(指针碰撞方式 serial parNew):所有用过的内存都放在一边,空闲的放到另一边,中间放着一个指针作为分界点指示器,在分配内存的时候把指针向空闲那边移动挪动一段与对象大小相等的距离。
不规整(空闲列表 cms):虚拟机维护一个列表,记录哪些内存是可用的在分配的时候找到一个足够大的空间划分给对象。
创建对象的并发环境下的线程安全问题:当正在给a对象分配内存的时候,指针还没有来得及修改,对象b又使用了原来的指针来分配内存的情况,
解决的方案:对分配空间的动作进行同步(实际上虚拟机采用的是cas配上失败重试的方式保证更新的原子性)
另一种是把内存分配的动作按照线程划分在不同的空间中,TLAB(本地线程分配缓冲)虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定;

分配完内存之后 ,虚拟机会对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码、对象的GC分代年龄等信息,这些信息都存储在对象头中;(对象头的内容稍后说,还有是否开启偏向锁);–这时在虚拟机中一个新对象产生了,从程序的角度来看,对象刚刚创建—-方法还没有执行,所有的字段都是0,然后执行方法,把对象按照程序猿的意愿进行初始化,这时一个真正可用的对象才算完全产生出来。

谈谈对象的内存布局:分为 对象头、实例数据、对齐填充(无用)
对象头:分为存储对象自身的运行时数据(哈希码、分代年龄、锁状态标志、线程持有的锁等)和类型指针(对象指向类元数据的指针,jvm通过这个指针来确定这个对象是哪个类的实例,并不是所有的jvm都必须在对象数据上保留类型指针,查找对象的元数据信息并不一定要经过对象本身),如果对象是数组类型,对象头中还应该存储数组长度的数据,java对象的大小可以通元数据确定,但数组从元数据中无法确定大小。
实例数据部分:储存对象真正有效的信息,即各种类型字段内容无论是父类还是子类的都要记录,存储顺序也受虚拟机分配策略参数(相同的宽度的字段总被分配到一起)和源码中定义的顺序有关系。

谈谈对象的访问定位:
java程序需要通过reference数据来操作堆上的具体对象,但jvm规范中并没有定义这个引用应该通过何种方式区定位、访问堆中的对象的具体位置,所以对象的访问取决于jvm的实现,目前主流是两种,使用句柄和指针两种;
句柄:使用句柄访问时,堆中会存在一个句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据的具体地址信息;看下图
jvm 对象的创建 布局 访问

直接引用:reference中直接存储对像的地址
jvm 对象的创建 布局 访问