java多线程(2) - Java的对象模型

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结构:
oops

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对象

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 _markunion _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

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。
400_ac3_932

在这种设计下,JVM对内存的分配和回收,都可以采用统一的方式来管理。

2579123-5b117a7c06e83d84

内存存储

对象的实例(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);
}

20170615230126453

总结

每一个java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了两部分信息,对象头和元数据。对象头有一些运行时的数据,其中就包括跟多线程相关的锁的信息。元数据维护的是指针,指向的是对象所属的类的instanceKlass。

comments powered by Disqus