JAVA对象模型、对象头
参考自:https://www.cnblogs.com/iou123lg/p/9190572.html
java虚拟机有很多对应的实现版本,这里的内容基于HotSpot虚拟机。HotSpot的底层是用C++ 实现的,可在源码确认。
我们都知道java和C++ 都是面向对象的语言,那么java在对象在虚拟机的表示,最简单一种实现就是在C++层面上实现一个与之对应的类,然而HotSpot并没有这么实现,而是专门设计了一套OOP-Klass二分模型。
OOP:ordinary object pointer,普通对象指针,用来描述兑现实例信息。
Klass:Java类的C++ 对等体,用来描述Java类。
之所以这么设计,其中一个理由是作者不想让每一个对象都有一个C++ 虚函数指针(vtable 虚函数表),下面是klass.hpp中的一段注释:
// One reason for the oop/klass dichotomy in the implementation is
// that we don't want a C++ vtbl pointer in every object. ……….
对于OOP对象来说,主要职能是表示对象的实例信息,没必要持有任何虚函数;而在描述java类的Klass兑现中含有VTBL(继承自klass_vtbl),那么klass就可以根据java对象的实际类型进行C++ 的分发,这样OOP对象只需要通过相应的Klass便可找到所有的虚函数,就避免了给每一个对象都分配一个C++ 的虚函数指针。
深入理解多线程(二)—— Java的对象模型
Klass向JVM提供了两个功能:
- 实现语言层面的java类;
- 实现java对象的分发功能;
这两个功能在一个C++ 类中就能实现,前者在Klass中已经实现,而后者就由Klass的子类提供虚函数实现(这是klass.hpp中的一段注释)
// A Klass provides:
// 1: language level class object (method dictionary etc.)
// 2: provide vm dispatch behavior for the object
// Both functions are combined into one C++ class.
OOP框架的关系可以在oopHierarchy.hpp文件中体现,JDK1.7和JDK1.8由于内存空间的变化(永久代?),所以oopsHierarchy.hpp的实现也不一样,这里以OpenJDK1.7开描述OOP-Klass。
typedef class oopDesc* oop;//oops基类
typedef class instanceOopDesc* instanceOop; //Java类实例
typedef class methodOopDesc* methodOop; //Java方法
typedef class constMethodOopDesc* constMethodOop; //Java方法不变信息
typedef class methodDataOopDesc* methodDataOop; //性能信息数据结构
typedef class arrayOopDesc* arrayOop; //数组oops基类
typedef class objArrayOopDesc* objArrayOop; //数组oops对象
typedef class typeArrayOopDesc* typeArrayOop;
typedef class constantPoolOopDesc* constantPoolOop;
typedef class constantPoolCacheOopDesc* constantPoolCacheOop;
typedef class klassOopDesc* klassOop; //与Java类对等的C++类
typedef class markOopDesc* markOop; //Java对象头
typedef class compiledICHolderOopDesc* compiledICHolderOop;
在java程序运行的过程中,每创建一个java对象,在JVM内部就会相应的创建一个OOP对象来表示该java对象。OOP对象的基类就是oopDesc,他的代码实现如下:
volatile markOop _mark;
union _metadata {
wideKlassOop _klass;
narrowOop _compressed_klass;
} _metadata;
在虚拟机内部,通过instanceOopDesc来表示一个Java对象。对象在内部中的布局可以分为两个连续的部分:
- instanceOopDesc(对象头)
- 实例数据
instanceOopDesc又被称为对象头,继承自oopDesc,看看instanceOop.hpp的实现,未新增的数据结构,和oopDesc一样,包含如下两部分信息:
_mark : markOop类型,存储对象运行时记录信息,主要有HashCode、分代年龄、锁状态标记、线程持有的锁、偏向线程ID等,占用内存和虚拟机位长一致(32或64),如果是32位虚拟机则为32位,以此类推。详细看看JVM原理。
_metadata:联合体,指向描述类型的Klass对象的指针,因为Klass对象包含了实例对象所属类型的元数据,古被称为元数据指针。虚拟机运行时将频繁使用这个指针定位代方法区的类信息。
到此基本描述了java的对象头,但是这只是一部分,还有一分部是klass,合起来才是完整的对象类型。那么klass在对象模型中是如何体现的呢?实际上,HotSpot是这样处理数据的,通过为每一个已加载的java类创建一个instanceKlass对象,用来在JVM层表示java类。
这是instanceKlass的数据结构:
// Method array.方法列表
objArrayOop _methods;
// Int array containing the original order of method in the class file (for
// JVMTI).方法顺序
typeArrayOop _method_ordering;
// Interface (klassOops) this class declares locally to implement.实现接口
objArrayOop _local_interfaces;
// Interface (klassOops) this class implements transitively.继承接口
objArrayOop _transitive_interfaces;
…………
typeArrayOop _fields;
// Constant pool for this class.
constantPoolOop _constants;
// Class loader used to load this class, NULL if VM loader used.
oop _class_loader;
// Protection domain.
oop _protection_domain;
可以看到,一个类该有的内容,instanceKlass基本都有了。
综上,java对象在JVM中的表示是:对象的实例(instanceOopDesc)存储在堆上,对象的元数据(instanceKlass)存储在方法区,对象的引用存储在栈上。如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hVK8Gj5J-1573375977066)(https://note.youdao.com/yws/api/personal/file/6829DC58567E4DB996BB4DB72D2E79B8?method=download&shareKey=b04fe5a9ca95de952ed3774ed3c34918)]