zoukankan      html  css  js  c++  java
  • 《OOP-Klass》

    1、JVM中OOP-KLASS模型

    在JVM中,使用了OOP-KLASS模型来表示java对象,即:
    1.jvm在加载class时,会创建instanceKlass,表示其元数据,包括常量池、字段、方法等,存放在方法区;instanceKlass是jvm中的数据结构;
    2.在new一个对象时,jvm创建instanceOopDesc,来表示这个对象,存放在堆区,其引用,存放在栈区;它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象instanceOopDesc对应java中的对象实例
    3.HotSpot并不把instanceKlass暴露给Java,而会另外创建对应的instanceOopDesc来表示java.lang.Class对象,并将后者称为前者的“Java镜像”,klass持有指向oop引用(_java_mirror便是该instanceKlass对Class对象的引用);
    4.要注意,new操作返回的instanceOopDesc类型指针指向instanceKlass,而instanceKlass指向了对应的类型的Class实例的instanceOopDesc;有点绕,简单说,就是Person实例——>Person的instanceKlass——>Person的Class。

    instanceOopDesc,只包含数据信息,它包含三部分:
    1. 对象头,也叫Mark Word,主要存储对象运行时记录信息,如hashcode, GC分代年龄,锁状态标志,线程ID,时间戳等;
    2. 元数据指针,即指向方法区的instanceKlass实例 (虚拟机通过这个指针来群定这个对象是哪个类的实例。)
    3. 实例数据;
    4. 另外,如果是数组对象,还多了一个数组长度

    实例说明

    复制代码
    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);
    }
    复制代码

    方法区:类信息、类变量(静态变量和常量)、方法
    堆:对象、成员变量
    栈:局部变量
    (1)当程序运行时,首先通过类装载器加载字节码文件,经过解析后装入方法区!在方法区中存了类的各种信息,包括类变量、常量及方法。对于同一个方法的调用,同一个类的不同实例调用的都是存在方法区的同一个方法。类变量的生命周期从程序开始运行时创建,到程序终止运行时结束!
    (2)当程序中new一个对象时,这个对象存在堆中,对象的变量存在栈中,指向堆中的引用!对象的成员变量都存在堆中,当对象被回收时,对象的成员变量随之消失!
    (3)当方法调用时,JVM会在栈中分配一个栈桢,存储方法的局部变量。当方法调用结束时,局部变量消失!

    类变量:属于类的属性信息,与类的实例无关,多个实例共用同一个类变量,存在与方法区中。类变量用static修饰,包括静态变量和常量。静态变量有默认初始值,常量必须声明同时初始化。

    成员变量:属于实例的变量,只与实例有关,写在类下面,方法外,非static修饰。成员变量会随着成员的创建而生存,随着成员的回收而销毁。

    局部变量:声明在方法中,没有默认初始值,随着方法的调用而创建,存储于栈中,随着方法调用的结束而销毁。

    参考:Class实例在堆中还是方法区中

        图解JAVA对象的创建过程

            深入理解Java类型信息(Class对象)与反射机制

            JAVA中类、实例与Class对象

           


    对象在 JVM 中的表示: OOP-Klass 模型

     

    - 简书http://blog.csdn.net/linxdcn/article/details/72850375 本人基于上文做了简单的整理, 解释及拓展, 方便像和我一样不熟悉 C++ 的...

     

    http://blog.csdn.net/linxdcn/article/details/72850375
    本人基于上文做了简单的整理, 解释及拓展, 方便像和我一样不熟悉 C++ 的人能更好的理解原文中的一些概念

    介绍

    HotSpot 是基于 c++ 实现,而 c++ 是一门面向对象的语言,本身具备面向对象基本特征,所以 Java 中的对象表示,最简单的做法是为每个 Java 类生成一个 c++ 类与之对应。

    但 HotSpot JVM 并没有这么做,而是设计了一个 OOP-Klass Model。这里的 OOP 指的是 Ordinary Object Pointer (普通对象指针),它用来表示对象的实例信息看起来像个指针实际上是藏在指针里的对象。而 Klass 则包含元数据和方法信息,用来描述 Java 类

    之所以采用这个模型是因为 HotSopt JVM 的设计者不想让每个对象中都含有一个 vtable(虚函数表),所以就把对象模型拆成 klass 和 oop,其中 oop 中不含有任何虚函数,而 Klass 就含有虚函数表,可以进行 method dispatch

    OOP-Klass 模型是对象在 JVM 中的表示

    Klass

    Klass: 包含元数据和方法信息,用来描述 Java 类

    Klass 主要有两个功能:

    • 实现语言层面的 Java 类
    • 实现 Java 对象的分发功能

    一般 jvm 在加载 class 文件时,会在方法区创建 instanceKlass,表示其元数据,包括常量池、字段、方法等

    OOP

    OOP: Ordinary Object Pointer (普通对象指针),它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象

    Klass 是在 class 文件在加载过程中创建的,OOP 则是在 Java 程序运行过程中 new 对象时创建的。

    一个 OOP 对象包含以下几个部分:

    • instanceOopDesc,也叫对象头
      • Mark Word,主要存储对象运行时记录信息,如 hashcode, GC 分代年龄,锁状态标志,线程 ID,时间戳等
      • 元数据指针,即指向方法区的 instanceKlass 实例
    • 实例数据

    目的

    HotSopt JVM 的设计者不想让每个对象中都含有一个 vtable(虚函数表),所以就把对象模型拆成 klass 和 oop,其中 oop 中不含有任何虚函数,而 Klass 就含有虚函数表,可以进行 method dispatch。

    关键词解析

    vtable: C++ 中每一个有虚函数的类, 都有 vtable(虚函数表)

    虚函数: 同 Java 方法(函数),C++ 中的有虚函数的概念,用 virtual 关键字来表示,每个类都会有一个虚函数表,该虚函数表首先会从父类中继承得到父类的虚函数表, 如果子类中重写了父类的虚函数(不管重写后的函数是否为虚函数),要调用哪个虚函数,是根据当前实际的对象来判断的(不管指针所属类型是否为当前类,有可能是父类型),指针当前指向的是哪种类型的对象,就调用哪个类型中类定义的虚函数。每个类只有一张虚拟函数表,所有的对象共用这张表。在 Java 中, 自动实现了虚函数这一概念: 多态——父类的变量, 调用子类的方法

    纯虚函数: 同 Java 抽象方法,C++ 中主要特征是不能被用来声明对象, 是抽象类, 是用来确保程序结构与应用域的结构据具有直接映射关系的设计工具。带有纯虚函数的类称为抽象类,抽象类能被子类 继承使用,在子类中必须给出纯虚函数的实现,如果子类未给出该纯虚函数的实现,那么该子类也是抽象类,只有在子类不存在纯虚函数时,子类才可以用来声明对 象!抽象类也能用于声明指针或引用,或用于函数声明中。具有抽象类特性的类还有构造函数和析构函数,全部是保护的类。如果没有给出纯虚函数的实现,则在它 所在的类的构造函数或析构函数中不能直接或间接的调用它。纯虚函数的实现可以在类声明外进行定义

    抽象类: 同 Java 抽象类,C++ 中指同时含有纯虚函数和非纯虚函数的类

    纯虚类: 同 Java 接口,C++ 中指只含有纯虚函数的类

    C++Java
    虚函数 普通函数
    纯虚函数 抽象函数
    抽象类 抽象类
    纯虚类 接口

    拓展: C++ 中普通函数不存在多态——不会根据实际的对象来判断调用函数, 而是直接调用当前变量类型的方法

    实例说明

    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);
    }

    上述代码得 OOP-Klass 模型入下所示

    一个对象(实例)对应一个 OOP对象,OOP对象的元数据指针是一样的...指向方法区的同一个  instanceKlass...


    JVM 系列 -(二)OOP-KLASS

    参考:

    学习 JVM 是如何从入门到放弃的? - Java3y 的个人空间 - 开源中国

    https://www.zhihu.com/question/50258991/answer/120450561

    总结:

    1. 模型如下

    1.java Object 为堆中的实例对象 2.InstanceKlass 为加载阶段,类的元数据 3.JAVA mirror 为加载阶段生成的元数据对应的 Class 对象

    2.OOP

    在 Java 程序运行的过程中,每创建一个新的对象,在 JVM 内部就会相应地创建一个对应类型的 oop(普通对象指针) 对象。各种 oop 类的共同基类为 oopDesc 类。

    在 JVM 内部,一个 Java 对象在内存中的布局可以连续分成两部分:对象头(instanceOopDesc) 和实例数据(成员变量)。

    instanceOopDesc 对象头包含两部分信息:Mark Word 和 元数据指针 (Klass*):

    volatile markOop  _mark;
      union _metadata {
        Klass*      _klass;
        narrowKlass _compressed_klass;
      } _metadata;

    1.Mark Word:它用于存储对象的运行时记录信息,如哈希值、GC 分代年龄 (Age)、锁状态标志(偏向锁、轻量级锁、重量级锁)、线程持有的锁、偏向线程 ID、偏向时间戳等。
    2. 元数据指针:instanceOopDesc 中的 _metadata 成员,它是联合体,可以表示未压缩的 Klass 指针 (_klass) 和压缩的 Klass 指针。对应的 klass 指针指向一个存储类的元数据的 Klass 对象。

    3.Klass

    每个 Java 对象的对象头里,_klass 字段会指向一个 VM 内部用来记录类的元数据用的InstanceKlass 对象;InsanceKlass 里有个_java_mirror 字段,指向该类所对应的 Java 镜像——java.lang.Class 实例。HotSpot VM 会给 Class 对象注入一个隐藏字段 “klass”,用于指回到其对应的 InstanceKlass 对象。这样,klass 与 mirror 之间就有双向引用,可以来回导航
    这个模型里,java.lang.Class 实例并不负责记录真正的类元数据,而只是对 VM 内部的 InstanceKlass 对象的一个包装供 Java 的反射访问用
    在 JDK 6 及之前的 HotSpot VM 里,静态字段依附在 InstanceKlass 对象的末尾;而在 JDK 7 开始的 HotSpot VM 里,静态字段依附在 java.lang.Class 对象的末尾。

    PS1:

    讲讲 JAVA 代码执行顺序

    public class XiaqiTest{
        public static void main(String[] args) {
          Xiaqi xiaqi=new Xiaqi();
          xiaqi.setName(“夏奇”)
        }
    }

    1、通过java.exe运行 xiaqiTest.Class,随后被加载到 JVM 中,元空间存储着类的信息 (包括类的名称、方法信息、字段信息..)。
    2、然后 JVM 找到 xiaqiTest 的主函数入口 (main),为 main 函数创建栈帧,开始执行 main 函数
    3、main 函数的第一条命令是 Xiaqi xiaqi=new Xiaqi();就是让 JVM 创建一个 xiaqi 对象,但是这时候方法区中没有 Xiaqi 类的信息,所以 JVM 马上加载 Xiaqi 类,把 Xiaqi 类的类型信息放到方法区中 (元空间)
    4、加载完 Xiaqi 类之后,Java 虚拟机做的第一件事情就是在堆区中为一个新的 xiaqi 实例分配内存, 然后调用构造函数初始化 Xiaqi 实例,这个 Xiaqi 实例持有着指向方法区的 Xiaqi类的类型信息(其中包含有方法表,java 动态绑定的底层实现)的引用
    5、当使用 xiaqi.setName("夏奇");的时候,JVM 根据 xiaqi 引用找到 xiaqi 对象,然后根据 xiaqi 对象持有的引用定位到方法区中 xiaqi 类的类型信息的方法表,获得setName()函数的字节码的地址
    6、为setName()函数创建栈帧,开始运行setName()函数


    Java 对象模型 OOP-Klass

    OOP-Klass Model(Ordinary Object Point-Klass Model)指的是普通对象指针,用来描述 java 类和对象在 JVM 中的表现形式,OOP 用来表示 java 实例在 JVM 中的表现,Klass 用来表示类在 JVM 中的表现。之所以要一分为二的设计是因为想要避免每个 Java 对象都存在一个虚函数,所以 oop 实例没有虚函数,而 Klass 类有虚函数,虚函数则是实现多态的关键所以 Java 最终也是通过虚函数来实现多态的,下面来分别深入了解下。

    本文基于 jdk1.8 hotspot, hostspot 源码下载地址

    OOP

    OOP(Ordinary Object Point)普通对象指针,它的定义如下,提取了需要讲解的部分关键代码

    class oopDesc {
     private:
      // mark word 相关信息
      volatile markOop  _mark;
      // 元数据
      union _metadata {
        // 实例对应的 Klass (实例对应的类)的指针
        Klass*      _klass;
        // 压缩指针
        narrowKlass _compressed_klass;
      } _metadata;
    
     private:
      // field addresses in oop
      // 私有的读取实例数据的方法实现在 oop.inline.hpp 中
      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;
      Metadata** metadata_field_addr(int offset) const;
     
     // 公有的读取或者写入实例数据的方法
     public:
      void byte_field_put(int offset, jbyte contents);
    
      jchar char_field(int offset) const;
      void char_field_put(int offset, jchar contents);
    
      jboolean bool_field(int offset) const;
      void bool_field_put(int offset, jboolean contents);
    
      jint int_field(int offset) const;
      void int_field_put(int offset, jint contents);
    };
    复制代码

    由此我们可以看到 oop 主要包括了 3 部分内容

    我们将下面的分成 3 块内容来讲

    • 对象头
      • mark word
      • _metadata 元数据指针
    • 插入实列数据以及获取实列数据

    对象头,markOop _mark

    mark 也就是 mark word 是对象头的一部分在 markOop.hpp 中它含有哪些内容呢,源码注释定义如下

    为了方便大家观看,盗图 2 张(来自并发编程艺术)

    结合源码注释可以看到,第一张图是 32 位虚拟机下 Mark Word 的表示,图 2 是 64 位 Mark Word 的表示

    对象头,union _metadata

    元数据指针,它包含了 2 部分内容,klass 和 _compressed_klass 他们指向了对象所属的类,具体的在 Klass 中讲解

    public 的 *_filed 和 对应的 put 方法

    查找和插入实例数据方法,举一个列子来看,其它的都是类似的

    jint int_field(int offset) const;
      void int_field_put(int offset, jint contents);
    复制代码

    实现在 oop.inline.hpp 中

    inline jint oopDesc::int_field(int offset) const { 
        return *int_field_addr(offset); 
    }
    
    inline void oopDesc::int_field_put(int offset, jint contents) { 
        *int_field_addr(offset) = contents;  
    }
    
    inline jint* oopDesc::int_field_addr(int offset) const { 
        return (jint*) field_base(offset);
    }
    
    inline void* oopDesc::field_base(int offset) const { 
        return (void*)&((char*)this)[offset]; 
    }
    复制代码

    可以看到上面的方法可以根据传入的 offset 在内存中,查找或者插入指定的数据并且返回其内存地址

    由上面这些内容可以看出,oop 其实就是描述了类的实例,包含了对象头,实例数据等,而由于在 java 中我们的对象类型有很多种,比如数组对象,自定义对象等等同时也对应了不同的 oop,部分数据如下

    // 各种 oop 的基类
    typedef class oopDesc*                            oop;
    // 表示一个 java 实例
    typedef class   instanceOopDesc*            instanceOop;
    // 表示一个数组实例
    typedef class   arrayOopDesc*                    arrayOop;
    // 表示一个对象数组实例
    typedef class     objArrayOopDesc*            objArrayOop;
    // 表示一个容纳基本类型的数组
    typedef class     typeArrayOopDesc*            typeArrayOop;
    复制代码

    上面这些 oop 都是继承与 oopDesc 在 jdk1.7 hotspot 中没有其它的操作,就只是一个空继承,在 jdk1.8 hotspot,有增加一些方法,我们挑选 1 个来看看,感兴趣的可以去看看源码

    class instanceOopDesc : public oopDesc {
     public:
      // aligned header size.
      static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
    
      // If compressed, the offset of the fields of the instance may not be aligned.
      static int base_offset_in_bytes() {
        // offset computation code breaks if UseCompressedClassPointers
        // only is true
        return (UseCompressedOops && UseCompressedClassPointers) ?
                 klass_gap_offset_in_bytes() :
                 sizeof(instanceOopDesc);
      }
    
      static bool contains_field_offset(int offset, int nonstatic_field_size) {
        int base_in_bytes = base_offset_in_bytes();
        return (offset >= base_in_bytes &&
                (offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
      }
    };
    复制代码

    所以,当我们使用 new 创建一个自定义的 Java 对象的时候,会创建一个 instanceOopDesc 来表示这个实列,当使用 new 创建一个数组时候会创建一个 objArrayOopDesc 来表示数组这个实列...

    元数据

    讲 oop 的时候我们说过,它有一个元数据指针指向它所属的类,能够想到,我们创建对象的时候有时候是有一些常量,方法等信息会保存在元数据中,元数据的定义如下

    // The metadata hierarchy is separate from the oop hierarchy
    
    //      class MetaspaceObj
    // 这是关于 oop 的一些信息
    // 表示一个 java 方法中不变的信息包括方法名、方法的访问修饰符、字节码、行号表、局部变量表等等
    class   ConstMethod;
    // 主要用于存储某些字节码指令所需的解析(resolve)好的常量项,
    // 例如给[get|put]static、[get|put]field、
    // invoke[static|special|virtual|interface|dynamic]等指令对应的常量池项用
    class   ConstantPoolCache;
    // 记录了 Java 方法执行时候的性能相关的 profile 信息,包括条件跳转是否总是走一个分支
    // 某处判断从不为 null 等信息
    class   MethodData;
    //      class Metadata
    class   Method;
    // class 文件中的常量池
    class   ConstantPool;
    //      class CHeapObj
    class   CompiledICHolder;
    复制代码

    可以看到 MetaspaceObj 分类下主要是用于描述 oop 相关的信息,Metadata 主要用于描述 Klass 相关信息

    Klass

    Klass 系统结构如下

    // The klass hierarchy is separate from the oop hierarchy.
    
    class Klass;
    class   InstanceKlass;
    class     InstanceMirrorKlass;
    class     InstanceClassLoaderKlass;
    class     InstanceRefKlass;
    class   ArrayKlass;
    class     ObjArrayKlass;
    class     TypeArrayKlass;
    复制代码

    class 向 JVM 提供了 2 个功能

    • 实现语言层面的 Java 类(在 Klass 基类中已经实现)
    • 实现 Java 对象的分发功能(由 Klass 的子类提供虚函数实现

    Klass 又是继承于 Metadata,因此像 Method、ConstantPool 都会以成员变量(或指针)的形式存在于 klass 体系中。

    由此我们来总结一下,当我们 new 一个对象的时候,JVM 首先会判断这个类是否加载没有的话会进行加载并创建一个 instanceKlass 对象来描述 Java 类用它可以获取到类对应的元数据信息如运行时常量池等信息,然后到初始化的时候会创建一个 instanceOopDesc 来表示这个 Java 对象实例,然后会进行 oop 的填充,包括填充对象头,使元数据指针指向 instanceKlass 类,然后填充实例数据。 创建完成后,instanceKlass 保存在方法区中,instanceOopDesc 保存在堆中,对象的引用则保存在栈中

    talk is cheap ,show me the code :

    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);
    }
    复制代码

    上面这张图是借用别人的,在其它文章也出现了多次,其中 instanceOopDesc(也称为对象头)包含 Mark Word 和元数据指针,个人认为不准确,instanceOopDesc 在源码中的定义还包含了实例数据等信息,如果个人理解有错误还望指出,感谢。

    参考资料


     

    深入探究 JVM | klass-oop 对象模型研究

     

    | 「浮生若梦」 - sczyh30's blog最近对 JVM 兴趣大增(其实是想回归 C 艹的怀抱了)~ 当我们在写 Java 代码的时候,我们会面对着无数个接口,类,对象和方法。

    最近对 JVM 兴趣大增(其实是想回归 C 艹的怀抱了)~

    当我们在写 Java 代码的时候,我们会面对着无数个接口,类,对象和方法。但我们有木有想过,Java 中的这些对象、类和方法,在 HotSpot JVM 中的结构又是怎么样呢?HotSpot JVM 底层都是 C++ 实现的,那么 Java 的对象模型与 C++ 对象模型之间又有什么关系呢?今天就来分析一下 HotSpot JVM 中的对象模型:oop-klass model,它们的源码位于 openjdk-8/openjdk/hotspot/src/share/vm/oops 文件夹内。

    注:本文对应的 OpenJDK 版本为 openjdk-8u76-b02。对于不同的版本 (openjdk-7, openjdk-8, openjdk-9),其对应的 HotSpot JVM 的对象模型有些许差别(7 和 8 的差别比较大)

    HotSpot JVM 并没有根据 Java 实例对象直接通过虚拟机映射到新建的 C++ 对象,而是设计了一个 oop-klass model。

    当时第一次看到 oop,我的第一反应就是 object-oriented programming,其实这里的 oop 指的是 Ordinary Object Pointer(普通对象指针),它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象。而 klass 则包含 元数据和方法信息,用来描述 Java 类。

    那么为何要设计这样一个一分为二的对象模型呢?这是因为 HotSopt JVM 的设计者不想让每个对象中都含有一个 vtable(虚函数表),所以就把对象模型拆成 klass 和 oop,其中 oop 中不含有任何虚函数,而 klass 就含有虚函数表,可以进行 method dispatch。这个模型其实是参照的 Strongtalk VM 底层的对象模型。

    在 oopsHierarchy.hpp 里定义了 oop 和 klass 各自的体系。

    这是 oop 的体系:

    typedef class oopDesc*                            oop;
    
    typedef class   instanceOopDesc*            instanceOop;
    
    typedef class   arrayOopDesc*                    arrayOop;
    
    typedef class     objArrayOopDesc*            objArrayOop;
    
    typedef class     typeArrayOopDesc*            typeArrayOop;

    注意由于 Java 8 引入了 Metaspace,OpenJDK 1.8 里对象模型的实现与 1.7 有很大的不同。原先存于 PermGen 的数据都移至 Metaspace,因此它们的 C++ 类型都继承于 MetaspaceObj 类 (定义见 vm/memory/allocation.hpp),表示元空间的数据。

    这是元数据的体系:

    class   ConstMethod;
    
    class   ConstantPoolCache;
    
    class   MethodData;
    
    class   Method;
    
    class   ConstantPool;
    
    class   CompiledICHolder;

    这是 klass 的体系:

    class Klass;
    
    class   InstanceKlass;
    
    class     InstanceMirrorKlass;
    
    class     InstanceClassLoaderKlass;
    
    class     InstanceRefKlass;
    
    class   ArrayKlass;
    
    class     ObjArrayKlass;
    
    class     TypeArrayKlass;

    注意 klass 代表元数据,继承自 Metadata 类,因此像 MethodConstantPool 都会以成员变量(或指针)的形式存在于 klass 体系中。

    以下是 JDK 1.7 中的类在 JDK 1.8 中的存在形式:

    • klassOop -> Klass*
    • klassKlass 不再需要
    • methodOop -> Method*
    • methodDataOop -> MethodData*
    • constMethodOop -> ConstMethod*
    • constantPoolOop -> ConstantPool*
    • constantPoolCacheOop -> ConstantPoolCache*

    一个 Klass 对象代表一个类的元数据(相当于 java.lang.Class 对象)。它提供:

    • language level class object (method dictionary etc.)
    • provide vm dispatch behavior for the object

    所有的函数都被整合到一个 C++ 类中。

    Klass 对象的继承关系:xxxKlass <:< Klass <:< Metadata <:< MetaspaceObj

    klass 对象的布局如下:

    // Klass layout:
    // [C++ vtbl ptr ] (contained in Metadata)
    // [layout_helper ]
    // [super_check_offset ] for fast subtype checks
    // [name ]
    // [secondary_super_cache] for fast subtype checks
    // [secondary_supers ] array of 2ndary supertypes
    // [primary_supers 0]
    // [primary_supers 1]
    // [primary_supers 2]
    // ...
    // [primary_supers 7]
    // [java_mirror ]
    // [super ]
    // [subklass ] first subclass
    // [next_sibling ] link to chain additional subklasses
    // [next_link ]
    // [class_loader_data]
    // [modifier_flags]
    // [access_flags ]
    // [last_biased_lock_bulk_revocation_time] (64 bits)
    // [prototype_header]
    // [biased_lock_revocation_count]
    // [_modified_oops]
    // [_accumulated_modified_oops]
    // [trace_id]
     
       

    oop 类型其实是 oopDesc*。在 Java 程序运行的过程中,每创建一个新的对象,在 JVM 内部就会相应地创建一个对应类型的 oop 对象。各种 oop 类的共同基类为 oopDesc 类。

    在 JVM 内部,一个 Java 对象在内存中的布局可以连续分成两部分:instanceOopDesc 和实例数据。instanceOopDesc 和 arrayOopDesc 又称为 对象头。

    instanceOopDesc 对象头包含两部分信息:Mark Word 和 元数据指针 (Klass*):

    volatile markOop  _mark;
    
    union _metadata {
    
        Klass*      _klass;
    
        narrowKlass _compressed_klass;
    
      } _metadata;

    分别来看一下:

    • Mark Word:instanceOopDesc 中的 _mark 成员,允许压缩。它用于存储对象的运行时记录信息,如哈希值、GC 分代年龄 (Age)、锁状态标志(偏向锁、轻量级锁、重量级锁)、线程持有的锁、偏向线程 ID、偏向时间戳等。
    • 元数据指针:instanceOopDesc 中的 _metadata 成员,它是联合体,可以表示未压缩的 Klass 指针 (_klass) 和压缩的 Klass 指针。对应的 klass 指针指向一个存储类的元数据的 Klass 对象。

    下面我们来分析一下,执行 new A() 的时候,JVM native 层里发生了什么。首先,如果这个类没有被加载过,JVM 就会进行类的加载,并在 JVM 内部创建一个 instanceKlass 对象表示这个类的运行时元数据(相当于 Java 层的 Class 对象)。到初始化的时候(执行 invokespecial A::<init>),JVM 就会创建一个 instanceOopDesc 对象表示这个对象的实例,然后进行 Mark Word 的填充,将元数据指针指向 Klass 对象,并填充实例变量。

    根据对 JVM 的理解,我们可以想到,元数据—— instanceKlass 对象会存在元空间(方法区),而对象实例—— instanceOopDesc 会存在 Java 堆。Java 虚拟机栈中会存有这个对象实例的引用。

     
     

     
    差不多搞清楚了
     
     
     
  • 相关阅读:
    Java User Thread and Daemon Thread
    BFS 和 DFS
    fail-fast vs fail-safe iterator in Java
    通过先序遍历和中序遍历后的序列还原二叉树
    单例模式总结
    TCP性能陷阱
    数据库事务的四大特性和事务隔离级别
    深入理解Java虚拟机- 学习笔记
    字符串,引用变量与常量池
    深入理解Java虚拟机- 学习笔记
  • 原文地址:https://www.cnblogs.com/cx2016/p/13129366.html
Copyright © 2011-2022 走看看