深入理解Java虚拟机——HotSpot虚拟机对象探秘

对象的创建

过程如下:

  1. JVM遇到new指令,检查这个指令的参数是否能在常量池定位到一个类的符号引用,检查这个符号代表的类是否被加载、解析和初始化过(否则执行类加载)。
  2. 检查通过,新生对象分配内存。对象所需内存大小在类加载完成后便可完全确定(每个变量+对象头引用等等)。
  3. 不同的JV由于GC策略不同,分配内存结果不同。绝对规整的JVM Heap,有一个指针作为空闲内存和已用内存的分界点,分配时挪动指针一段与对象大小相等的距离,这种方式叫指针碰撞。如果不规整,VM会有一个列表标记内存的可用,从列表找到一块足够大的空间分配给对象实例,这种方式叫空闲列表
  4. 考虑到并发性,有两种解决方案,一种是对分配内存空间的同步进行同步处理,第二种是把内存分配的动作按照线程划分为不同的空间进行,叫做本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)。分配完后,内存空间初始化为零值。
  5. 一般执行完new指令,然后执行init方法。

对象的内存布局

对象内存中包括三部分:对象头(Header)、实例数据(Instance Data)和对齐数据(Padding), 这些都在堆上

对象头

Header中包括两部分,一部分是存储对象自身的运行时数据:哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID、偏向时间戳等,这部分数据长度在32位和64位虚拟机分别为32bit和64bit(未开启压缩指针)。这一部分数据叫Mark Word.会尽量采用了位复用,保存这部分数据,不同状态下存储的内容不一样。

所以一个引用的长度在32位jvm和Davlik虚拟机是32位,在未开启指针压缩的64bit jvm是64位的(8个字节),这是在栈上的

对象头另一部分就是类型指针,即对象指向它的类元数据的指针,但不是所有的虚拟机都必须在对象数据上保留类型指针,比如Java数组,对象头还包括一块记录数据长度的数据。因为数据的元数据中无法确定数组的大小。所以这一部分不是必须的。

实例数据

实例数据部分是对象真正存储的有效信息。无论是父类继承还是子类定义的,都会记录下来。存储顺序会受到虚拟机分配策略参数和字段在java源码中定义的顺序有关,默认分配策略是longs/doubles, ints, shorts/chars, bytes/boolean, oops,相同宽度的字段总是被分配到一起。

对齐填充

对齐填充不是必然存在的,也没有特别意义。起到占位符的作用,HotSpot虚拟机自动内存管理系统要求对象起始地址必须是8字节的整数倍。

对象的访问定位

两种,一种是使用句柄,另一种是直接指针。

句柄

java堆里将会华为一块内存来作为句柄池,引用里存的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息。好处是reference里存放的是稳定的句柄地址,不需要修改。

句柄

直接指针

reference里存储的直接就是对象地址,好处是速度快,节省了一次指针定位的时间开销。这种方式虚拟机用的更多一些。比如Hotspot虚拟机

直接指针

Eclipse里模拟OOM

设置eclipse run的jvm参数如下

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

//加入heapDumpOnOutOfMemoryError,虚拟机会在出现内存溢出时dump当前的内存堆转储快照,以便进行分析
-XX:+HeapDumpOnOutOfMemoryError

使用Eclipse Memory Analyzer可以进行内存分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值