java对象保存在堆内存中。在内存中,一个Java对象包含三部分:对象头、实例数据和对象填充。
对象头中包含锁状态标志、线程持有的锁等标志。
Java的对象模型
在JVM的内存结构中,对象保存在堆内存中。我们在对对象进行操作时,操作的是对象的引用。
那么对象本身在JVM中的结构是什么样的?
oop-klass model
HotSpot JVM的设计者不想让每个对象中都含有一个vtable(虚函数表)(HotSpot基于C++实现),所以设计了一个OOP-Klass Model来表示对象。OOP(Ordinary Object Pointer)指的是普通对象指针,Klass用来描述对象实例的具体类型。
C++在运行时不维护类型信息,所以在编译时直接在子类的虚函数表中将被子类重写的方法替换掉。
Java在运行时会维护类型信息和类的继承体系,每一个类在方法区中会对应一个数据结构用于存放类的信息,可以通过Class对象访问这个数据结构。而每一个在堆上创建的对象,都具有一个指向方法区类型信息数据结构的指针,通过这个指针可以确定对象的类型。
oop-klass结构
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。
在Java程序运行过程中,每创建一个新的对象,在JVM内部就会相应的创建一个对应类型的OOP对象
这些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分代等信息。
klass
//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提供两个功能:
- 实现语言层面的Java类(在Klass基类中已经实现)
- 实现Java对象的分发功能(由Klass的子类提供虚函数实现)
HotSpot JVM的设计者把对象拆为klass和oop,其中oop的职能主要在于表示对象的示例数据,其中不包含虚函数。而klass为了实现虚函数多态,提供了虚函数表。_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针,这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。
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。