HotSpot JVM 「02」Java Object Layout
01-Klass and OOP
HotSpot JVM 实现中用 Klass-OOP 模型来表示 Java 中的类和对象。
图 1. Klass 的继承体系
图 2. oopDesc 的继承体系
Klass 结构是 .class 文件的运行时结构; oopDesc 结构是 Object 对象的运行时结构。
oopDesc(对象头)中包含了两部分信息(更多详细的内容参考[1]):
mark word,存储了哈希码、锁信息、GC 元数据等。
klass word,类信息。注:应该是指针,指向 Metaspace 中的类结构。
possible alignment paddings,非必须
对象头后存储的是对象的实例数据。
图 3. oopDesc 布局示意图
JVM 中的普通对象表示为 instanceOopDesc,数组对象表示为 arrayOopDesc。两者在结构上的区别是,后者多了 4 个字节的长度信息。
02-Java Object Layout
在 32/64 位机器上,mark word 是有区别的(更多详细信息[1]):
图 4. 32 bits mark word 可能的分配情况及其含义
图 5. 64 bits mark word 可能的分配情况及其含义
无锁 → 偏向锁 → 轻量级锁 → 重量级锁 升级过程(不可降级)?更多详细参考[1]
偏向锁获取:CAS 比较线程 ID,同一线程再次获得锁的效率提高。
偏向锁获取失败时,说明有其他线程加入抢锁的队伍,在到达 safepoint 后获得偏向锁的线程被挂起,判断锁对象是否处于锁定状态,并据此决定撤销偏向锁或升级为轻量级锁。
轻量级锁(自旋锁):
借助工具查看 Java 对象内存布局?
03-对象创建过程
在所有会创建对象的场景中,该类的某个特定构造器方法会被调用。
创建类的实例时,会在堆上开辟空间存放所有的 instance variables,包括其超类声明的实例变量(也包括被子类 hide 的变量)。
实例变量被初始化为默认值
在新创建对象的引用被返回之前,调用特定的 constructor(先执行 instance initializers 和 instance variable initializers)
内存分配方式有哪些呢?
若堆是规整的,即使用过得放在一边,空闲未用的放在一边,在两者之间存在一个指针,称为分界指示器。分配方式就是指示器向空闲部分移动一定的大小。这种方式称为指针碰撞。
若堆不是规整的,空闲内存块列表记录在空闲列表中,分配方式为从空闲列表中取合适的大小分配给对象。
访问对象的方式有哪些呢?
通过句柄访问,栈中对象的引用指向的是句柄地址,句柄包含了实例数据与数据类型信息。对象移动时更方便。
直接访问,栈中对象的引用指向的是实例数据的地址。访问速度更高。
如何判断对象已经死亡?
引用计数,最直接,最简单,但不能解决循环引用问题,而且算法边界很多,不容易实现
可达性分析,从 GC Root 开始,根据引用关系向下搜索。GC Root 包括:栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中 JNI 引用的对象、JVM 内部的引用例如 Class 对象、所有被 synchronized 持有的对象、JMX 内部中注册的回调等等。
应用类型?更多信息参考 [1]
强引用
软引用
弱引用
虚引用
如何判断一个类不再使用?同时满足以下三个条件:
类的所有实例已被回收
类的加载器已被回收(通常难以达成,除非特殊设计目的存在,例如 OSGI、JSP 的重加载等)
类对应的 Class 对象不被应用,也不能通过反射访问这个类
历史文章推荐
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/b8d58fe72759ce744cb01161c】。文章转载请联系作者。
评论