java对象保存在堆内存中。在内存中,一个Java对象包含三部分:对象头、实例数据和对象填充。
对象头中包含锁状态标志、线程持有的锁等标志。
在JVM的内存结构中,对象保存在堆内存中。我们在对对象进行操作时,操作的是对象的引用。
那么对象本身在JVM中的结构是什么样的?
HotSpot JVM的设计者不想让每个对象中都含有一个vtable(虚函数表)(HotSpot基于C++实现),所以设计了一个OOP-Klass Model来表示对象。OOP(Ordinary Object Pointer)指的是普通对象指针,Klass用来描述对象实例的具体类型。
C++在运行时不维护类型信息,所以在编译时直接在子类的虚函数表中将被子类重写的方法替换掉。
Java在运行时会维护类型信息和类的继承体系,每一个类在方法区中会对应一个数据结构用于存放类的信息,可以通过Class对象访问这个数据结构。而每一个在堆上创建的对象,都具有一个指向方法区类型信息数据结构的指针,通过这个指针可以确定对象的类型。
oop-klass结构:
oop体系:
//定义了oops共同基类 typedef class oopDesc* oop; //表示一个Java类型实例 typedef class instanceOopDesc* instanceOop; //表示一个Java方法 typedef class methodOopDesc* methodOop; //表示一个Java方法中的不变信息 typedef class constMethodOopDesc* constMethodOop; //记录性能信息的数据结构 typedef class methodDataOopDesc* methodDataOop; //定义了数组OOPS的抽象基类 typedef class arrayOopDesc* arrayOop; //表示持有一个OOPS数组 typedef class objArrayOopDesc* objArrayOop; //表示容纳基本类型的数组 typedef class typeArrayOopDesc* typeArrayOop; //表示在Class文件中描述的常量池 typedef class constantPoolOopDesc* constantPoolOop; //常量池告诉缓存 typedef class constantPoolCacheOopDesc* constantPoolCacheOop; //描述一个与Java类对等的C++类 typedef class klassOopDesc* klassOop; //表示对象头 typedef class markOopDesc* markOop;
上面是整个oops模块的组成结构,其中包括多个子模块,每个子模块对应一个类型,每个类型的oop都代表一个在JVM内部使用的特定对象的类型。
OOPS类的共同基类是oopDesc。
这些OOPS在JVM内部有者不同的用途,例如instanceOopDesc表示类实例,arrayOopDesc表示数组。
也就是说,当我们用new创建一个Java对象实例的时候,JVM会创建一个instanceOopDesc对象来表示这个Java对象。同理,对于数组实例则是arrayOopDesc。
oopDesc类的定义:
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
wideKlassOop _klass;//普通指针
narrowOop _compressed_klass;//压缩类指针
} _metadata;
private:
// field addresses in oop
void* field_base(int offset) const;
jbyte* byte_field_addr(int offset) const;
jchar* char_field_addr(int offset) const;
jboolean* bool_field_addr(int offset) const;
jint* int_field_addr(int offset) const;
jshort* short_field_addr(int offset) const;
jlong* long_field_addr(int offset) const;
jfloat* float_field_addr(int offset) const;
jdouble* double_field_addr(int offset) const;
address* address_field_addr(int offset) const;
}
class instanceOopDesc : public oopDesc {
}
class arrayOopDesc : public oopDesc {
}
instanceOopDesc主要包含 markOop _mark 和 union _metadata ,示例数据则保存在oopDesc中定义的各种field中。
_mark保存了锁标志、GC分代等信息。
//klassOop的一部分,用来描述语言层的类型 class Klass; //在虚拟机层面描述一个Java类 class instanceKlass; //专有instantKlass,表示java.lang.Class的Klass class instanceMirrorKlass; //专有instantKlass,表示java.lang.ref.Reference的子类的Klass class instanceRefKlass; //表示methodOop的Klass class methodKlass; //表示constMethodOop的Klass class constMethodKlass; //表示methodDataOop的Klass class methodDataKlass; //最为klass链的端点,klassKlass的Klass就是它自身 class klassKlass; //表示instanceKlass的Klass class instanceKlassKlass; //表示arrayKlass的Klass class arrayKlassKlass; //表示objArrayKlass的Klass class objArrayKlassKlass; //表示typeArrayKlass的Klass class typeArrayKlassKlass; //表示array类型的抽象基类 class arrayKlass; //表示objArrayOop的Klass class objArrayKlass; //表示typeArrayOop的Klass class typeArrayKlass; //表示constantPoolOop的Klass class constantPoolKlass; //表示constantPoolCacheOop的Klass class constantPoolCacheKlass;
Klass类是其他klass类型的父类。
Klass向JVM提供两个功能:
HotSpot JVM的设计者把对象拆为klass和oop,其中oop的职能主要在于表示对象的示例数据,其中不包含虚函数。而klass为了实现虚函数多态,提供了虚函数表。_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针,这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。
JVM在运行时,需要一种用来标识Java内部类型的机制。HotSpot的解决方案是,为每一个已加载的Java类创建一个instanceKlass对象,用来在JVM层表示Java类。
//类拥有的方法列表
objArrayOop _methods;
//描述方法顺序
typeArrayOop _method_ordering;
//实现的接口
objArrayOop _local_interfaces;
//继承的接口
objArrayOop _transitive_interfaces;
//域
typeArrayOop _fields;
//常量
constantPoolOop _constants;
//类加载器
oop _class_loader;
//protected域
oop _protection_domain;
....
在JVM中,对象在内存中的基本存在形式就是oop,对象所属的类也是一种对象,即klassOop。对于klassOop也有一个对应的klass来描述。就是klassKlass。
在这种设计下,JVM对内存的分配和回收,都可以采用统一的方式来管理。
对象的实例(instanceOopDesc)保存在堆上,对象的元数据(instantKlass)保存在方法区,对象的引用保存在栈上。 方法区用于存储虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。
class Model
{
public static int a = 1;
public int b;
public Model(int b) {
this.b = b;
}
}
public static void main(String[] args) {
int c = 10;
Model modelA = new Model(2);
Model modelB = new Model(3);
}
每一个java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了两部分信息,对象头和元数据。对象头有一些运行时的数据,其中就包括跟多线程相关的锁的信息。元数据维护的是指针,指向的是对象所属的类的instanceKlass。