读《深入理解Java虚拟机》笔记(二)Jvm的对象管理

3 篇文章 0 订阅

对象的创建

虚拟机遇到一条new执行命令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类符号的引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化。如果没有那么就先必须执行相应的类加载过程,以后再讨论。

再类加载通过后,虚拟机将为新生对象分配内存,对象所需内存的大小早类加载完成后便可以完全确定。虚拟机维护着一个列表,记录上那些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。选择那种分配方式是有java堆是否规整决定的,而java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。如果规整的化就会采用简单的“指针碰撞”来分配。

由于对象创建是一个频繁的行为,在并发的情况下不一定是线程安全的,所以采用以下两种解决方案:一种是对分配内存空间的动作进行同步处理-----实际上虚拟机采用cas配上失败重试的方式保证更新操作的原子性;另一种是把内存分配的动作按照线程划分在不同的空间之中进行,每个线程在堆中预先分配一小块内存,称为本地线程分配缓冲(TLBA)。虚拟机是否使用TLBA,可以通过-XX:+/UseTLBA参数来设定。

内存分配完成后,虚拟机要将分配到的内存空间都初始化为零值(不包括对象头),这样就保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

接下来,虚拟机设置对象头信息。例如这个对象是那个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象Gc分代年龄等信息。执行<init>方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象产生了

对象的内存布局

  在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头,实例数据,和对齐填充(padding)。

  对象头包含两部分,第一部分用于存储对象自身的运行时数据,如哈希码,Gc分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等。官方称它为“MarkWord”。被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,会根据对象的状态,复用自己的存储空间。另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是数组还需要在对象头中有一块用于记录数组长度的数据。因为从数组的元数据中无法确定数组的大小。

  实例部分是对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容。这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在源码中定义顺序的影响。HotSpot默认的分配策略是longs/doubles,ints,shorts/chars,bytes/booleans.oops(Oodinary Object Pointers)。相同宽度的字段总是被分配到一块,父类中定义的变量会出现在子类之前,如果CompactFields参数值为true,子类中较窄的变量也会插入到父类变量的空隙之中。

  对齐填充并不是必然存在的。仅仅起着占位符的作用,由于hotSpot的自动内存管理系统要求对象起始地址必须是8字节的正整数。

对象的访问定位

  对象的访问方式取决于虚拟机的实现方式。目前主流的访问方式有两中,句柄和直接指针。

句柄访问,在堆中将会划出一块内存来作为句柄池引用中存储就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息见下图。

  句柄访问的最大好处就是引用中存储的稳定的句柄地址,在对象移动时只会改变句柄中的实例数据指针,而引用本身不需要更改。

  直接指针访问,引用中存储的直接就是对象地址。最大好处就是速度更快,节省了一次指针定位的时间开销,hotspot使用的就是这种。见图

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值