精华内容
下载资源
问答
  • Yang Zhang 扩展的 Garman Klass Volatility
  • Android-Klass 北京交通大学安卓班项目
  • klass对象模型.png

    2020-07-12 16:51:07
    javav oop模型 思维导图..............................................
  • 大师杯 项目设置 npm install 编译和热重装以进行开发 npm run serve 编译并最小化生产 npm run build 整理和修复文件 npm run lint 自定义配置 请参阅。 Vue-Master-Klass
  • 经典的经典的Garman Klass volatility 波动率算法很好用,MATLAB
  • 前端项目-klass.zip

    2019-09-02 11:55:19
    前端项目-klass,Class provider with classical inheritance interface
  • 前端开源库-node-klass

    2019-08-29 17:48:58
    前端开源库-node-klass节点klass,类库的伪extjs
  • JVM-oop-klass模型

    2020-12-18 22:27:02
    一、oop-klass模型 oop-klass模型是JVM底层的数据结构,理解JVM的必要概念 • Klass表示Java类在JVM中的存在形式 • InstanceKlass表示类的元信息 • InstanceMirrorKlass表示类的Class对象 • ...

    JAVA运行时环境逻辑图

    在这里插入图片描述

    一、oop-klass模型

    oop-klass模型是JVM底层的数据结构,理解JVM的必要概念

    • Klass表示Java类在JVM中的存在形式
        • InstanceKlass表示类的元信息
    		• InstanceMirrorKlass表示类的Class对象
    		• InstanceRefKlass表示?
    	• ArrayKlass表示数组类的元信息
    		• TypeArrayKlass表示基本数组类的元信息
    		• ObjArrayKlass表示引用数组类的元信息
    • oopDesc表示JAVA对象在JVM中的存在形式
    	• instanceOopDesc表示普通类对象(非数组类对象)
    	• arrayOopDesc表示数组类对象
    		• typeArrayOopDesc表示基本数组类对象
    		• objArrayOopDesc表示引用数组类对象
    

    下面是这几个Klass的继承关系图
    在这里插入图片描述

    下面是这几个oop的继承关系图
    在这里插入图片描述

    1. InstanceKlass

    class InstanceKlass: public Klass {
    friend class VMStructs;
    friend class ClassFileParser;
    friend class CompileReplay;
    protected:
    // Constructor
    InstanceKlass(int vtable_len,
    int itable_len,
    int static_field_size,
    int nonstatic_oop_map_size,
    ReferenceType rt,
    AccessFlags access_flags,
    bool is_anonymous);
    ...
    protected:
    // Annotations for this class
    Annotations*    _annotations;
    Klass*          _array_klasses;
    ConstantPool* _constants;Array* inner_classes;
    char*           _source_debug_extension;
    Symbol*         _array_name;
    int             _nonstatic_field_size;
    int             _static_field_size;    // number words used by static fields (oop and non-oop) in this klass
    u2              _generic_signature_index;
    u2              _source_file_name_index;
    u2              _static_oop_field_count;// number of static oop fields in this klass
    u2              _java_fields_count;    // The number of declared Java fields
    int             _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks
    bool            _is_marked_dependent;  // used for marking during flushing and deoptimization
    bool            _has_unloaded_dependent;
    enum {
    _misc_rewritten                = 1 << 0, // methods rewritten.
    _misc_has_nonstatic_fields     = 1 << 1, // for sizing with UseCompressedOops
    _misc_should_verify_class      = 1 << 2, // allow caching of preverification
    misc_is_anonymous             = 1 << 3, // has embedded host_klass field
    misc_is_contended             = 1 << 4, // marked with contended annotation
    misc_has_default_methods      = 1 << 5, // class/superclass/implemented interfaces has default methods
    misc_declares_default_methods = 1 << 6  // directly declares default methods (any access)
    };
    u2              misc_flags;
    u2              minor_version;        // minor version number of class file
    u2              major_version;        // major version number of class file
    Thread*         init_thread;          // Pointer to current thread doing initialization (to handle recusive initialization)
    int             vtable_len;           // length of Java vtable (in words)
    int             itable_len;           // length of Java itable (in words)
    OopMapCache*    volatile oop_map_cache;   // OopMapCache for all methods in the klass (allocated lazily)
    MemberNameTable* member_names;        // Member names
    JNIid*          jni_ids;              // First JNI identifier for static fields in this class
    jmethodID*      methods_jmethod_ids;  // jmethodIDs corresponding to method_idnum, or NULL if none
    nmethodBucket*  dependencies;         // list of dependent nmethods
    nmethod*        osr_nmethods_head;    // Head of list of on-stack replacement nmethods for this class
    BreakpointInfo* breakpoints;          // bpt lists, managed by Method*
    GrowableArray<PreviousVersionNode > previous_versions;
    JvmtiCachedClassFileData* cached_class_file;
    volatile u2     _idnum_allocated_count;         change
    u1              _init_state;                    // state of class
    u1              _reference_type;                // reference type
    JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map;  // JVMTI: used during heap iteration
    NOT_PRODUCT(int _verify_count;)  // to avoid redundant verifies
    Array<Method> _methods;
    Array<Method> _default_methods;
    Array<Klass> _local_interfaces;
    Array<Klass> transitive_interfaces;
    Array*     method_ordering;Array*     default_vtable_indices;
    Array*      _fields;
    

    这里贴一下这些成员变量的中文解释
    _annotations:保存该类的所有注解
    _array_klasses:保存数组元素所关联的klass指针
    _constants:保存该类的常量池指针
    _inner_classes:保存内部类相关的信息
    _array_name:如果该类是数组,就会生成数组类名词,如“[Ljava/lang/String;”
    _nonstatic_field_size:非静态字段数量
    _static_field_size:静态字段数量
    _generic_signature_index:泛型签名在常量池中的索引
    _source_file_name_index:文件名在常量池中的索引
    _static_oop_field_count:该类包含的静态的引用类型字段个数
    _java_fields_count:已声明的Java字段数量
    _nonstatic_oop_map_size:非静态oop映射块的大小(以字为单位)
    _is_marked_dependent:用于刷新和反优化期间打标
    _minor_version:主版本号
    _major_version:次版本号
    _init_thread:初始化此类的线程
    _vtable_len:虚函数表的大小
    _itable_len:接口函数表的大小
    _oop_map_cache:该类所有方法的OopMapCache(延迟分配)
    _member_names:MemberNameTable指针
    _jni_ids:存放jni_id单向链表的首地址(什么是jni_id?)
    _methods_jmethod_ids:与method_idnum对应的jmethodIDs,如果没有,则为NULL
    _dependencies:存放nmethod的Bucket的首地址
    _osr_nmethods_head:栈上替换nmethods的链表的首地址
    _breakpoints:断点链表首地址
    _previous_versions:此实例的前一个版本的有趣部分的数组。请参见下面的PreviousVersionWalker
    _cached_class_file:缓存的类文件
    _idnum_allocated_count:已经分配的idnum的个数
    _init_state:该类的状态,值:allocated(已分配内存但未链接)、loaded(加载并插入到类层次结构中但仍未链接)、linked(验证及链接成功但未初始化)、being_initialized(正在初始化)、fully_initialized(已完成初始化)、initialization_error(初始化出错)
    _reference_type:引用类型
    _methods:存储该类的所有方法对象的指针的数组指针
    _default_methods:存储从接口继承的所有方法对象的指针的数组指针
    _local_interfaces:数组指针,存储所有实现的接口的指针
    _transitive_interfaces:数组,存储直接实现的接口指针+接口间继承实现的接口指针
    _method_ordering:包含类文件中方法的原始顺序的Int数组,JVMTI需要用到
    _default_vtable_indices:默认构造方法在虚表中的索引
    _fields:类的成员属性

    2. InstanceMirrorKlass

    class InstanceMirrorKlass: public InstanceKlass {
      friend class VMStructs;
      friend class InstanceKlass;
     private:
      static int _offset_of_static_fields;
      // Constructor
      InstanceMirrorKlass(int vtable_len, int itable_len, int static_field_size, int nonstatic_oop_map_size, ReferenceType rt, AccessFlags access_flags,  bool is_anonymous)
        : InstanceKlass(vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, access_flags, is_anonymous) {}
     public:
      InstanceMirrorKlass() { assert(DumpSharedSpaces || UseSharedSpaces, "only for CDS"); }
      // Type testing
      bool oop_is_instanceMirror() const             { return true; }
    

    3. InstanceKlass和InstanceMirrorKlass

    InstanceKlass和InstanceMirrorKlass
    可以看出来
    • InstanceKlass中定义了Java运行时环境类所需的所有数据信息,比如 constants 常量池、methods 方法 等(An InstanceKlass is the VM level representation of a Java class. It contains all information needed for at class at execution runtime.)
    • InstanceMirrorKlass是InstanceKlass的一个子类
    • InstanceMirrorKlass是java.lang.Class类专用的InstanceKlass(An InstanceMirrorKlass is a specialized InstanceKlass for java.lang.Class instances.)
    简单总结一下,InstanceKlass包含了Java运行时环境中类所需的所有数据信息,在类加载这一步,类加载器会将.class文件读入类加载器的class content,然后以InstanceKlass的形式写入JVM内存区域的方法区中,而InstanceMirrorKlass是类所对应的Class对象(java.lang.Class)的InstanceKlass
    对于InstanceKlass,在Java中,对于所有类,类的内部属性轮廓是不是可以认为是一样的?类都有父类、实现接口、变量、方法、代码块等,而变量和方法也有属性,即可访问性(public、private、protected、default)、静态与非静态,还有数组形式的。无论如何,类的内部属性以及属性的属性,是有限的、可列举的,那么在JVM中以C++代码就可以用一个类来表示,它就是InstanceKlass,这一层是比较好理解的

    对于InstanceMirrorKlass的理解,首先需要理解什么是java.lang.Class,我们来看一下这个类的注释吧:Instances
    of the class Class represent classes and interfaces in a running Java
    application.
    翻译过来说,就是类或接口在Java运行时环境中的一个表达方式,再看看java.lang.Class的方法就知道,getConstructors()、getMethods()、getFields()、getDeclaredFields(),这些都是类的内部属性,且对于任何类来说,都可以用形如A.class的方法来获取java.lang.Class,即每个Java类都有一个java.lang.Class,那么在JVM中以C++代码表示,每个类的java.lang.Class就是InstanceMirrorKlass。java.lang.Class也是一个类,InstanceMirrorKlass是InstanceKlass的子类

    关于Class对象,JVM并没有将描述Java类元信息的instanceKlass直接暴露给Java程序使用,而是又抽象了一层,即所谓的镜像类:instanceMirrorKlass。jdk8以后,类的静态属性也由存储在instanceKlass实例中转为存放
    来看一下parseClassFile方法

    instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
                                                        ClassLoaderData* loader_data,
                                                       Handle protection_domain,
                                                        KlassHandle host_klass,
                                                        GrowableArray<Handle>* cp_patches,
                                                        TempNewSymbol& parsed_name,
                                                       bool verify,
                                                        TRAPS) {
    ……
       // Allocate mirror and initialize static fields
       java_lang_Class::create_mirror(this_klass, protection_domain, CHECK_(nullHandle));
    ……  
    

    来看一下create_mirror方法,第六行代码:初始化静态字段do_local_static_fields

    oop java_lang_Class::create_mirror(KlassHandle k, Handle protection_domain, TRAPS) {
    ……
       Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK_0);
    ……
         // Initialize static fields
         InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, CHECK_NULL);
    ……
    

    来看一下allocate_instance方法,从代码中可以看出来,是从堆区分配内存的(CollectedHeap::Class_obj_allocate),所以说Class对象是分配在堆上的

    instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) {
     // Query before forming handle.
     int size = instance_size(k);
      KlassHandle h_k(THREAD, this);
      instanceOop i = (instanceOop) CollectedHeap::Class_obj_allocate(h_k, size, k, CHECK_NULL);
     return i;
    }
    

    总结一下,类加载器将.class文件载入JVM中,parse后生成的是instanceKlass对象,之后会生成这个Klass类对应的镜像类实例,并将Java类中的静态变量初始化后存储在镜像类实例中,这个镜像类就是Java代码中的Class对象

    4. InstanceRefKlass

    描述java.lang.ref.Reference的子类,这部分的概念与强软弱虚引用、垃圾回收有关系

    5. ArrayKlass

    在理解了InstanceKlass后,这三个类就很好理解了
    • ArrayKlass表示的是数组类的元信息(ArrayKlass is the abstract baseclass for all array classes)
    • TypeArrayKlass表示基本数组类的元信息(A TypeArrayKlass is the klass of a typeArray, It contains the type and size of the elements)
    • ObjArrayKlass表示引用数组类的元信息(ObjArrayKlass is the klass for objArrays)

    class ArrayKlass: public Klass {
     friend class VMStructs;
    private:
     int      _dimension;         // This is n'th-dimensional array.
      Klass* volatile _higher_dimension;  // Refers the (n+1)'th-dimensional array (if present).
      Klass* volatile _lower_dimension;   // Refers the (n-1)'th-dimensional array (if present).
     int      _vtable_len;        // size of vtable for this klass
      oop      _component_mirror;  // component type, as a java/lang/Cl
    TypeArrayKlass
    class TypeArrayKlass : public ArrayKlass {
     friend class VMStructs;
    private:
      jint _max_length;            // maximum number of elements allowed in an array
     // Constructor
     TypeArrayKlass(BasicType type, Symbol* name);
     static TypeArrayKlass* allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS);
    public:
     TypeArrayKlass() {} // For dummy objects.
    ObjArrayKlass
    class ObjArrayKlass : public ArrayKlass {
     friend class VMStructs;
    private:
      Klass* _element_klass;            // The klass of the elements of this array type
      Klass* _bottom_klass;             // The one-dimensional type (InstanceKlass or TypeArrayKlass)
     // Constructor
     ObjArrayKlass(int n, KlassHandle element_klass, Symbol* name);
     static ObjArrayKlass* allocate(ClassLoaderData* loader_data, int n, KlassHandle klass_handle, Symbol* name, TRAPS);
    public:
     // For dummy objects
     ObjArrayKlass() {}
    6. oopDesc
    oopDesc表示JAVA对象在JVM中的存在形式
    class oopDesc {
     friend class VMStructs;
    private:
     volatile markOop  _mark;
     union _metadata {
        Klass*      _klass;
        narrowKlass _compressed_klass;
      } _metadata;
     // Fast access to barrier set.  Must be initialized.
     static BarrierSet* _bs;
    public:
      markOop  mark() const         { return _mark; }
      markOop* mark_addr() const    { return (markOop*) &_mark; }
     void set_mark(volatile markOop m)      { _mark = m;   }
     void    release_set_mark(markOop m);
      markOop cas_set_mark(markOop new_mark, markOop old_mark);
     void init_mark();
      Klass* klass() const;
      Klass* klass_or_null() const volatile;
      Klass** klass_addr();
      narrowKlass* compressed_klass_addr();
     void set_klass(Klass* k);
     // For klass field compression
     int klass_gap() const;
     void set_klass_gap(int z);
     // For when the klass pointer is being used as a linked list "next" field.
     void set_klass_to_list_ptr(oop k);
      oop list_ptr_from_klass();
     // size of object header, aligned to platform wordSize
     static int header_size()          { return sizeof(oopDesc)/HeapWordSize; }
     // Returns whether this is an instance of k or an instance of a subclass of k
     bool is_a(Klass* k)  const;
     // Returns the actual oop size of the object
     int size();
     // Sometimes (for complicated concurrency-related reasons), it is useful
     // to be able to figure out the size of an object knowing its klass.
     int size_given_klass(Klass* klass);
     // type test operations (inlined in oop.inline.h)
     bool is_instance()            const;
     bool is_instanceMirror()      const;
     bool is_instanceClassLoader() const;
     bool is_instanceRef()         const;
     bool is_array()               const;
     bool is_objArray()            const;
     bool is_typeArray()           const;
    ...
    instanceOopDesc
    instanceOopDesc表示普通类对象(非数组类对象)
    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);
      }
    };
    

    arrayOopDesc

    arrayOopDesc表示数组类对象

    class objArrayOopDesc : public arrayOopDesc {
     friend class ObjArrayKlass;
     friend class Runtime1;
     friend class psPromotionManager;
     friend class CSetMarkOopClosure;
     friend class G1ParScanPartialArrayClosure;
     template <class T> T* obj_at_addr(int index) const {
       assert(is_within_bounds(index), "index out of bounds");
       return &((T*)base())[index];
      }
    private:
     // Give size of objArrayOop in HeapWords minus the header
     static int array_size(int length) {
       const uint OopsPerHeapWord = HeapWordSize/heapOopSize;
       assert(OopsPerHeapWord >= 1 && (HeapWordSize % heapOopSize == 0),
              "Else the following (new) computation would be in error");
       uint res = ((uint)length + OopsPerHeapWord - 1)/OopsPerHeapWord;
    #ifdef ASSERT
       // The old code is left in for sanity-checking; it'll
       // go away pretty soon. XXX
       // Without UseCompressedOops, this is simply:
       // oop->length() * HeapWordsPerOop;
       // With narrowOops, HeapWordsPerOop is 1/2 or equal 0 as an integer.
       // The oop elements are aligned up to wordSize
       const uint HeapWordsPerOop = heapOopSize/HeapWordSize;
       uint old_res;
       if (HeapWordsPerOop > 0) {
          old_res = length * HeapWordsPerOop;
        } else {
          old_res = align_size_up((uint)length, OopsPerHeapWord)/OopsPerHeapWord;
        }
       assert(res == old_res, "Inconsistency between old and new.");
    #endif  // ASSERT
       return res;
      }
    

    typeArrayOopDesc

    typeArrayOopDesc表示基本数组类对象

    class typeArrayOopDesc : public arrayOopDesc {
    protected:
      jchar*    char_base()   const { return (jchar*)   base(T_CHAR); }
      jboolean* bool_base()   const { return (jboolean*)base(T_BOOLEAN); }
      jbyte*    byte_base()   const { return (jbyte*)   base(T_BYTE); }
      jint*     int_base()    const { return (jint*)    base(T_INT); }
      jlong*    long_base()   const { return (jlong*)   base(T_LONG); }
      jshort*   short_base()  const { return (jshort*)  base(T_SHORT); }
      jfloat*   float_base()  const { return (jfloat*)  base(T_FLOAT); }
      jdouble*  double_base() const { return (jdouble*) base(T_DOUBLE); }
     friend class TypeArrayKlass;
    public:
      jbyte* byte_at_addr(int which) const {
       assert(is_within_bounds(which), "index out of bounds");
       return &byte_base()[which];
      }
      jboolean* bool_at_addr(int which) const {
       assert(is_within_bounds(which), "index out of bounds");
       return &bool_base()[which];
      }
    

    objArrayOopDesc

    objArrayOopDesc表示引用数组类对象

    class arrayOopDesc : public oopDesc {
     friend class VMStructs;
     // Interpreter/Compiler offsets
     // Header size computation.
     // The header is considered the oop part of this type plus the length.
     // Returns the aligned header_size_in_bytes.  This is not equivalent to
     // sizeof(arrayOopDesc) which should not appear in the code.
     static int header_size_in_bytes() {
       size_t hs = align_size_up(length_offset_in_bytes() + sizeof(int),
                                  HeapWordSize);
    #ifdef ASSERT
       // make sure it isn't called before UseCompressedOops is initialized.
       static size_t arrayoopdesc_hs = 0;
       if (arrayoopdesc_hs == 0) arrayoopdesc_hs = hs;
       assert(arrayoopdesc_hs == hs, "header size can't change");
    #endif // ASSERT
       return (int)hs;
      }
    public:
     // The _length field is not declared in C++.  It is allocated after the
     // declared nonstatic fields in arrayOopDesc if not compressed, otherwise
     // it occupies the second half of the _klass field in oopDesc.
     static int length_offset_in_bytes() {
       return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
                                  sizeof(arrayOopDesc);
      }
    
    展开全文
  • oop-klass内存模型

    万次阅读 2019-03-01 16:02:33
    只要是对JVM有所了解的,或多或少的都知道oop-klass模型.那么什么是oop-klass模型? JVM内部基于oop-klass模型描述一个java类,将一个java类分为两个部分进行描述,其中第一个模型是oop,第二个模型是klass. 接下来,...

    只要是对JVM有所了解的,或多或少的都知道oop-klass模型.那么什么是oop-klass模型?

    JVM内部基于oop-klass模型描述一个java类,将一个java类分为两个部分进行描述,其中第一个模型是oop,第二个模型是klass.

    接下来,本文从以下几个角度进行讲解:

    1. 宏观: 从整体上描述一下oop-klass模型
    2. 微观: 从类的继承结构上进行讲述,加深理解

    宏观

    JVM使用oop-klass这种模型来描述一个java类.虽然模型有2个,但是确实从3个不同的角度去看待java类的:

    1. 实例数据角度: jvm使用oop来保存用户的实例数据
    2. 元数据角度: jvm将类的元数据保存到了klass模型中
    3. 方法分发规则: 为了实现多态,jvm 采用了 vtable,itable技术,而vtable,itable 就是保存在klass模型中.

    如图所示:
    oop-klass模型

    那么这里有一个知识点: 什么是vtable,itable呢?

    简要概括就是:

    vtable: 该类所有的函数(除了static, final)和 父类的函数虚拟表。

    Itable: 该类所有实现接口的函数列表.

    oop-klass体系概览

    JVM中使用了oop,klass,handle 这三个类来描述oop-klass.需要注意一点的是: 这三个类不仅能够描述外在的java类,也能描述JVM内在的类型(如 constantPoolOop).

    oop,klass之前有介绍了,那么handle是啥意思?

    handle 是对oop的行为的封装.这里需要注意的是:

    1. 大多数情况下,JVM在访问java类时是一定通过handle的_handle 来得到oop,再通过oop获得对应的klass,这样,handle就能够访问oop的函数了.
    2. 如果是调用JVM内部的c++类所对应的oop函数,则不需要通过handle,直接通过oop拿到指定的klass即可.

    其中的关系如下:

    oop-klass-handle

    微观

    oop

    oop 就是 ordinary object pointer,也即普通对象指针.oop成员众多,其类图如下:

    oop类图
    这里有个问题,不是说xxxoop吗?怎么类图上怎么就是xxxppDesc了?
    原因在于: oopsHierarchy.hpp 中进行了如下定义:

    typedef class oopDesc*                            oop; 
    typedef class   instanceOopDesc*            instanceOop; 
    typedef class   methodOopDesc*                    methodOop; 
    typedef class   constMethodOopDesc*            constMethodOop; 
    typedef class   methodDataOopDesc*            methodDataOop; 
    typedef class   arrayOopDesc*                    arrayOop; 
    typedef class     objArrayOopDesc*            objArrayOop; 
    typedef class     typeArrayOopDesc*            typeArrayOop; 
    typedef class   constantPoolOopDesc*            constantPoolOop; 
    typedef class   constantPoolCacheOopDesc*   constantPoolCacheOop; 
    typedef class   symbolOopDesc*                    symbolOop;
    typedef class   klassOopDesc*                    klassOop; 
    typedef class   markOopDesc*                    markOop; 
    typedef class   compiledICHolderOopDesc*    compiledICHolderOop;
    

    其中各类的说明如下:

    类名说明
    oop oop的顶级父类
    instanceOop 表示java类实例
    methodOop 表示java方法
    constMethodOop 表示java方法中的只读信息(其实就是字节码指令)
    methodDataOop 表示性能统计的相关数据
    arrayOop 数组对象,定义了数组oops的抽象基类
    objArrayOop 表示引用类型数组对象
    typeArrayOop 表示基本类型数组对象
    constantPoolOop 表示java字节码文件中的常量池
    constantPoolCacheOop 与constantPoolOop 相伴生,是后者的缓存对象,缓存了字段和方法的访问信息,为允许时环境快速访问字段和方法提供重要作用
    symbolOop symbolOop是规范化字符串。所有symbolOop都位于全局符号表中。
    klassOop 指向jvm内部的klass实例的对象
    markOop oop 的标记对象,表示对象头
    compiledICHolderOop 内联缓存实现的帮助器对象。它包含从编译到解释调用转换时使用的中间值(method+klass对),总是分配在永久区(以避免在清除期间遍历codecache)

    klass

    klass: 按照官方解释,klass 主要提供下面2种能力:

    1. klass 提供一个与java类对等的c++类型描述
    2. klass 提供虚拟机内部的函数分发机制

    其类图如下:

    klass类图

    handle

    handle 部分的类图如下:

    handle类图

    通过对比三者可以发现: handle 分别给oop和klass定义了不同的体系?这与我们之前给handle的定义有出入–> handle 是对oop的行为的封装.

    那么这部分的原因很简单: 在jvm中,使用oop-klass这种一分为二的模型去描述java类以及JVM内部的特殊类,为此JVM内部定义了各种oop和klass类型.但是,对于每一个oop,其实都是一个C++类型,也即klass;而对于每一个klass所对应的class,在JVM内部又会被封装成oop.

    JVM在加载类时,会使用oop去存储这个类型的实例数据,并使用klass去存储这个类型的元数据和虚方法表.而当一个类型完成其生命周期后,JVM会通过GC去回收,在回收时,既要回收一个类实例所对应的实例数据oop,也要回收其所对应的元数据和方法分发表(当然,二者的回收时机是不同的).为了让GC即能回收oop也能回收klass,因此oop本身被封装成oop,klass也被封装成oop.

    三者关系的实现

    1. handle与oop关联:

      在handle继承体系中, handle这个顶层类中,定义了如下字段:

       oop* _handle;
      

      而该字段会在实例化的时候,会通过构造器进行赋值:

      Handle::Handle(Thread* thread, oop obj) {
        assert(thread == Thread::current(), "sanity check");
        if (obj == NULL) {
          _handle = NULL;
           } else {
          _handle = thread->handle_area()->allocate_handle(obj);
        }
       }
       
      inline Handle::Handle(oop obj) {
        if (obj == NULL) {
          _handle = NULL;
        } else {
          _handle = Thread::current()->handle_area()->allocate_handle(obj);
        }
      }
      
    2. oop与klass进行关联:

      oopDesc做为oop体系的顶层类,其中定义了如下字段:

       union _metadata {
       wideKlassOop    _klass;
       narrowOop       _compressed_klass;
       } _metadata;
      

      而wideKlassOop类型为:klassOopDesc*
      narrowOop类型为:uint32_t(jdk 1.6)

      当开启了指针压缩时,会使用narrowOop,反之,使用wideKlassOop 来指向klassOopDesc.

      该字段会通过oopDesc::set_klass(klassOop k) 来进行设置,如下:

      inline void oopDesc::set_klass(klassOop k) {
        // since klasses are promoted no store check is needed
        assert(Universe::is_bootstrapping() || k != NULL, "must be a real klassOop");
        assert(Universe::is_bootstrapping() || k->is_klass(), "not a klassOop");
        if (UseCompressedOops) {
          oop_store_without_check(compressed_klass_addr(), (oop)k);
        } else {
          oop_store_without_check(klass_addr(), (oop) k);
        }
      }
      
    展开全文
  • 4、Klass 5、InstanceKlass 6、Method 7、Java vtable 8、Java itable 9、InstanceKlass特殊子类 10、ArrayKlass 在分析thread.cpp的create_vm函数中(参考《Hotspot启动和初始化源码解析》)发现...

    目录

    1、类继承结构

    2、MetaspaceObj

    3、Metadata

    4、Klass

    5、InstanceKlass

    6、Method

    7、Java vtable

    8、Java itable 

    9、InstanceKlass特殊子类

    10、ArrayKlass


         在分析thread.cpp的create_vm函数中(参考《Hotspot启动和初始化源码解析》)发现JVM通过initialize_class函数来加载Java类,该函数是threap.cpp的一个静态函数,其函数定义如下:

      接着为main_thread创建thread_object即Java中的java.lang.Thread对象时使用了create_initial_thread函数,该函数返回了oop,实际是类oopDesc* 的别名,如下图:

     在《Java程序员自我修养——内存模型》中探讨过,Java对象在内存中是实例数据和类型数据相分离的,实例数据保存了一个指向类型数据的指针,即OOP(ordinary object pointer),因此猜测这里的Klass就是所谓的类型数据,oopDesc就是具体的实例数据了。

    1、类继承结构

         在上述代码Klass处按crtl并点击即可进入到定义Klass的头文件kclass.hpp中,该文件的位于hotspot\src\share\vm\oops目录下,选中Klass,点击右键,选择Open Type Hierarchy即可显示该类的继承关系图了,如下:

    选择其中某一个类如MetaspaceObj,右键选择Open即可进入定义该父类的头文件alloction.hpp,位于hotspot\src\share\vm\memory下,如下图:

    2、MetaspaceObj

         该类是作为存放在Metaspace元空间的中类的基类,不能执行delete,否则会出现致命错误,注意该类没有定义虚函数。该类重载了new操作符,主要用于给共享只读的或者共享读写的类分配内存,该类定义了如下方法:

     该类定义了一个枚举Type用于表示对象类型,包含以下几种类型:

    3、Metadata

          Metadata是内部表示类相关元数据的一个基类,注意Metadata定义了多个虚函数,其定义的方法如下:

    其中identity_hash()方法返回的实际是该对象的内存地址,如下图:

     其中跟stack相关的三个方法是类重定义(class redefinition)期间使用的,跟Java栈帧无关。

    4、Klass

         一个Klass提供两方面的功能:实现Java语言层面的类和提供多态方法的支持。C++类实例通过保存typeinfo指针实现RTTI,通过vtbl指针实现多态,Hotspot的Oop-Klass模型将这两者整合到Klass中,Java类实例只需保留一个Klass指针即可实现RTTI和多态,能够有效降低指针的内存占用。大致方案是用Oop表示Java实例,主要用于表示实例数据,不提供任何虚函数功能,Oop保存了对应Kclass的指针,所有方法调用通过Klass完成并通过Klass获取类型信息,Klass基于C++的虚函数提供对Java多态的支持。Klass作为父类主要职责是描述了类的继承关系, 其包含的重要属性如下:

         _layout_helper:_layout_helper是对象内存布局的一个组合描述符,如果不是InstanceKclass或者ArrayKlass,则该值为0.对InstanceKclass而言,该值表示对象的以字节为单位的内存占用空间,对ArrayKlass而言,该值是一个组合起来的假数字,包含4部分,具体怎么组合和解析由子类实现:

    1. tag:如果数组元素是对象实例则是0x80,否则是0xC0
    2. hsz: 数组头元素的字节数
    3. ebt:数组元素的类型,枚举值BasicType
    4. esz:数组元素大小,以字节为单位

    该值因为被频繁查询,所以放在虚函数表指针的后面。

         _super_check_offset:用于快速查找supertype的一个偏移量

        _secondary_super_cache:Klass指针,上一次observed secondary supertype

        _secondary_supers:Klass指针数组,指向secondary supertype,即类实现的接口对应的Kclass

        _primary_supers:Klass指针数组,大小固定为8,指向primary supertypes,即默认继承的类如Object

        _name: 类名,如java/lang/String,[Ljava/lang/String

        _java_mirror:  oopDesc指针,此类对应的java/lang/Class实例,可以据此访问类静态属性

        _super:Klass指针,父类

        _subklass:Klass指针,子类

        _next_sibling:Klass指针,该类的下一个子类

        _next_link:Klass指针,ClassLoader加载的下一个Klass

        _class_loader_data :ClassLoaderData指针,加载该类的ClassLoader

        _modifier_flags: 修改标识,Class.getModifiers使用

        _access_flags:获取类的修饰符,如private类访问控制,final,static,abstract ,native等

    测试用例如下:

    package jvmTest;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    
    interface interTest{
        void show();
    }
    
    class Base{
        private int a=1;
        public void print(){
            System.out.println("Base");
        }
    }
    
    class Base2 extends Base{
        public int a;
    
        public Base2(int a) {
            this.a = a;
        }
        public void print(){
            System.out.println("Base2");
        }
    }
    
    class A extends Base2 implements interTest  {
        public int b;
        public A(int a,int b) {
            super(a);
            this.b=b;
        }
    
        @Override
        public void show() {
            System.out.println("a->"+a+",b="+b);
        }
        public void print(){
            System.out.println("A");
        }
    }
    
    class B extends A{
        private int c;
        public B(int a, int b) {
            super(a, b);
            c=3;
        }
        @Override
        public void show() {
            System.out.println("a->"+a+",b="+b+",c="+c);
        }
        public void print(){
            System.out.println("B");
        }
    }
    
    public class MainTest {
    
        public static void main(String[] args) {
            A a=new A(1,2);
            a.show();
            A[] a2={a,new B(2,3)};
            while (true){
                try {
                    System.out.println(getProcessID());
                    Thread.sleep(600*1000);
                } catch (Exception e) {
    
                }
            }
        }
    
        public static final int getProcessID() {
            RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            System.out.println(runtimeMXBean.getName());
            return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                    .intValue();
        }
    }

      使用HSDB Class Browser查看jvmTest.A的Kclass,如下图:

     使用Inspector可查看该Klass的各属性,如下图:

    比如_super属性,无法直观的看出该属性对应的类是什么类,可以选中该行,然后复制出@后的地址,在Code Viewer中查询,如下图:

         Klass定义了处理_layout_helper的各种工具方法,如layout_helper_is_instance,layout_helper_is_array等,定了处理_access_flag的各种工具方法,如is_abstract,is_interface等,定义跟Klass本身直接相关的方法,如is_subclass_of,is_subtype_of,find_field,lookup_method,verify,print_on等,多数是虚方法。

         在哪去找Oop了?可以通过Stack Memory查看线程栈中局部变量所指向的Oop,也可通过scanoops搜索指定类型的Oop,拿到地址后,通过Inspect查看,以Stack Memory为例说明:

    jvmTest.A的实例通过_metadata._compressed_klass属性保存了对jvmTest/A的InstanceKlass的引用,该实例有3个属性,分别是继承自Base的int a,继承自Base2的int a,jvmTest.A自己的int b。具体Oop的类定义和内存结构且听下回分解。

    5、InstanceKlass

         InstanceKlass是Java Class在JVM层面的内存表示,包含了类在执行过程中需要的所有信息。

         InstanceKlass在Klass基础上新增的重要属性如下:

         _annotations:Annotations指针,该类使用的所有注解

         _array_klasses: 数组元素为该类的数组Klass指针

        _constants: ConstantPool指针,该类的常量池

         _inner_classes:用一个ushort数组表示当前类的内部类属性和闭包(EnclosingMethod)属性,参考Class类中的getEnclosingXX、getDeclaredXX

         _array_name: 根据类名计算的以该类的数组的名字

        _nonstatic_field_size:非静态字段的内存大小,以heapOopSize为单位,默认使用压缩指针时heapOopSize是int的大小

       _static_field_size:静态字段的内存大小,以字宽(HeapWordSize,实际是一个指针变量的内存大小)为单位

       _generic_signature_index:常量池中保存该类的Generic signature的索引

       _source_file_name_index:包含此类的源文件名在常量池中索引

       _static_oop_field_count:此类的包含的静态引用类型字段的数量

        _java_fields_count:总的字段数量

       _nonstatic_oop_map_size:非静态的oop map block的内存大小,以字宽为单位

       _minor_version:此类的主版本号

       _major_version:此类的次版本号

       _init_thread:执行此类初始化的Thread指针

       _vtable_len:Java 虚函数表(vtable)的内存大小,以字宽为单位

       _itable_len:Java 接口函数表(itable)的内存大小,以字宽为单位

       _oop_map_cache:OopMapCache指针,该类的所有方法的OopMapCache

        _member_names: MemberNameTable指针,保存了成员名

       _jni_ids:JNIid指针,该类的第一个静态字段的JNIid,可以根据其_next属性获取下一个字段的JNIid

        _methods_jmethod_ids:jmethodID指针,java方法的ID列表

        _dependencies:nmethodBucket指针,依赖的本地方法,以根据其_next属性获取下一个nmethod

        _osr_nmethods_head:栈上替换的本地方法链表的头元素

        _cached_class_file:  class文件的内容,JVMTI retransform时使用

      _idnum_allocated_count:已经分配的方法的idnum的个数,可以根据该ID找到对应的方法,如果JVMTI有新增的方法,已分配的ID不会变

       _init_state:类的状态,是一个枚举值ClassState,allocated(已分配内存),loaded(从class文件读取加载到内存中),linked(已经成功链接和校验),being_initialized(正在初始化),fully_initialized(已经完成初始化),initialization_error(初始化异常)

        _reference_type:引用类型

        _methods:方法指针数组,类方法

       _default_methods:方法指针数组,从接口继承的默认方法

       _local_interfaces:Klass指针数组,直接实现的接口Klass

        _transitive_interfaces:Klass指针数组,所有实现的接口Klass,包含_local_interfaces和通过继承间接实现的接口

       _method_ordering:int数组,保存类中方法声明时的顺序,JVMTI使用

       _default_vtable_indices:默认方法在虚函数表中的索引

       _fields:类的字段属性,每个字段有6个属性,access, name index, sig index, initial value index, low_offset, high_offset,6个组成一个数组,access表示访问控制属性,根据name index可以获取属性名,根据initial value index可以获取初始值,根据low_offset, high_offset可以获取该属性在内存中的偏移量

        接下来几个属性是内嵌的在类中的,没有对应的属性名,只能通过指针和偏移量的方式访问:

       Java vtable:Java虚函数表,大小等于_vtable_len

       Java itables:Java接口函数表,大小等于 _itable_len

       非静态oop-map blocks ,大小等于_nonstatic_oop_map_size

       接口的实现类,仅当前类表示一个接口时存在,如果接口没有任何实现类则为NULL,如果只有一个实现类则为该实现类的Klass指针,如果有多个实现类,为当前类本身

        host klass,只在匿名类中存在,为了支持JSR 292中的动态语言特性,会给匿名类生成一个host klass。

    测试用例:

    package jvmTest;
    
    import javax.xml.bind.annotation.XmlElement;
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    
    interface interTest {
        void show();
    }
    
    interface interTest2 {
        void show2();
    }
    
    class Base {
        private int a = 1;
    
        public void print() {
            System.out.println("Base");
        }
    }
    
    class Base2 extends Base implements interTest2 {
        public int a;
    
        public Base2(int a) {
            this.a = a;
        }
    
        @Override
        public void print() {
            System.out.println("Base2");
        }
    
        @Override
        public void show2() {
            System.out.println("show2 Base2 ");
        }
    }
    
    class A extends Base2 implements interTest {
        @XmlElement
        public int b;
        public static int si = 10;
        public static String ss = "test";
    
        public A(int a, int b) {
            super(a);
            this.b = b;
        }
    
        @Override
        public void show() {
            System.out.println("a->" + a + ",b=" + b);
        }
    
        @Override
        public void print() {
            System.out.println("A");
        }
    
        public void print2() {
            System.out.println("A2");
        }
    }
    
    class B extends A {
        private int c;
    
        public B(int a, int b) {
            super(a, b);
            c = 3;
        }
    
        @Override
        public void show() {
            System.out.println("a->" + a + ",b=" + b + ",c=" + c);
        }
    
        @Override
        public void print() {
            System.out.println("B");
        }
    }
    
    public class MainTest {
    
        public static void main(String[] args) {
            A a = new A(1, 2);
            a.show();
            A[] a2 = {a, new B(2, 3)};
            while (true) {
                try {
                    System.out.println(getProcessID());
                    Thread.sleep(600 * 1000);
                } catch (Exception e) {
    
                }
            }
        }
    
        public static final int getProcessID() {
            RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            System.out.println(runtimeMXBean.getName());
            return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                    .intValue();
        }
    }

      在Inspector界面查找到_methods属性如下图:

       Array对象的_data属性是一个T[],_data的地址就是头元素的地址,如下图:

      可以用mem查看剩下的几个元素的地址,如下图:

       与Code View界面查看的方法地址一致,如下图:

    对应的_idnum_allocated_count的属性也是5,即总共5个方法,如下图:

    _local_interfaces属性的长度为1,如下图:

     _data[0]对应的Kclass就是interTest,如下图:

     _transitive_interfaces属性的长度为2,如下图:

    使用mem查看两个Klass的地址,如下图:

    第二个就是interTest,第一个对应的Klass如下图:

    interTest2不是jvmTest.A直接实现的接口,而是通过继承Base2间接实现的接口。

    _fields字段记录了所有字段的相关属性,单个field的各属性用一个类FieldInfo表示,在fieldInfo.hpp中定义,FieldInfo本身也是用一个u2数组来保存各属性,并定义了一个枚举来对应数组各索引的具体含义,如下图:

    根据这些offset计算属性的属性名或者初始化值,逻辑较为复杂,不能直观的判断,这里不做探讨。

    _java_fields_count的属性为3,jvmTest.A一共三个字段,b,si,ss,如下:

     

    _source_file_name_index的值为42,可以查看常量池中42对应的选项,如下图:

     _nonstatic_field_size大小为3,单位是heapOopSize,开启指针压缩时跟int大小一样,因为有Base和Base2各有一个int,A有一个int,总共3个。_static_field_size的大小1,单位是HeapWord,跟指针大小一样,在64位CPU下是8字节,A的static变量有两个,si是4字节,ss开启指针压缩后也是4字节,加起来是8字节。_static_oop_field_count为1,A只有一个静态的String类型的字段ss。

    heapOopSize的定义在globalDefinitions.hpp中,如下图:

    HeapWord的定义也在 globalDefinitions.hpp中,如下图:

     InstanceKlass除定义了与上述属性相关的方法外,还定义以下几类方法:

    • 获取类方法相关的,如method_with_idnum,find_method,find_method_by_name,find_method_impl等
    • 获取字段属性相关的,如field_name, find_field, find_interface_field等
    • 类加载相关的,如link_class_impl,verify_code,initialize_impl等
    • 用于遍历oop,垃圾回收使用的oop_oop_iterate系列方法,如oop_oop_iterate_nv(oop,G1ParScanClosure)
    • 根据Klass创建Oop的方法,如allocate_instance,allocate_objArray等

    6、Method

         Method用于表示一个Java方法,因为一个应用有成千上万个方法,因此保证Method类在内存中短小非常有必要。为了本地GC方便,Method把所有的指针变量和方法大小放在Method内存布局的前面,方法本身的不可变数据如字节码用ConstMethod表示,可变数据如Profile统计的性能数据等用MethodData表示,都通过指针访问。如果是本地方法,Method内存结构的最后是native_function和signature_handler,按照解释器的要求,这两个必须在固定的偏移处。Method没有子类,定义在method.hpp文件中,其类继承关系如下图:

          Method包含的属性如下:

          _constMethod:ConstMethod指针,该类定义constMethod.hpp中,用于表示方法的不可变的部分,如方法ID,方法的字节码大小,方法名在常量池中的索引等,注意其中_constMethod_size的单位为字宽,_code_size的单位是字节,其内存结构如下图,因为异常检查表平均长度小于2,本地变量表大多数情况下没有,所以这两个没有被压缩保存。访问这些内嵌表都很快,不是性能瓶颈。ConstMethod提供了获取内嵌部分如字节码的起始地址,然后可以据此方法字节码了。

        _method_data:MethodData指针,该类在methodData.hpp中定义,用于表示一个方法在执行期间收集的相关信息,如方法的调用次数,在C1编译期间代码循环和阻塞的次数,Profile收集的方法性能相关的数据等。

        _method_counters:MethodCounters指针,该类在methodCounters.hpp中定义,用于记录方法调用次数,方法抛出异常的次数,方法断点的个数,主要用于基于调用频率的热点方法的跟踪统计。

         _access_flags:AccessFlags类,表示方法的访问控制标识

        _vtable_index: 该方法在vtable表中的索引

       _method_size:这个Method对象的大小,以字宽为单位

       _compiled_invocation_count:被编译成本地方法后调用的次数

       _i2i_entry:解释器的入口地址

       _adapter:此方法在解释器和编译器执行的适配器

       _from_compiled_entry:执行编译后的代码的入口地址

       _code:nmethod类指针,表示该方法编译后的本地代码

       _from_interpreted_entry:code ? _adapter->i2c_entry() : _i2i_entry的缓存

       以上一节的示例中的print2()方法为例进行分析,Class Brower中找到该方法的地址,然后在Inspector中查看,如下图:

    其中_name_index即方法名在常量池的索引是39,方法签名在常量池的索引是37,常量池对应的数据如下图:

    方法签名中()表示方法入参,V表示方法无返回值。

    _max_locals即方法栈帧中本地变量的最大个数,为1,因为方法中只有一个本地变量,字符串A2, _max_stack即方法栈帧的对象的最大个数,为2,这个是方法的字节码决定的,第一步是获取System的out对象将其入栈,第二步是将字符串A2入栈,第三步将已入栈的两个对象作为入参调用println方法,所以栈帧的最大深度是2,

    使用inspect命令查看print2方法的地址,如下图:

    size是88,这个size是用sizeof算出来的,单位是字节,_method_size是11,单位是字段,8字节,两者一致。

    接着用mem命令查看具体的内存数据,如下图:

    第一个8字节是kclass header,第二个8字节就是属性_constMethod的值了,第三和第四个8字节都是0,即空指针,对应methodData属性和methodCounters属性,两者都是空指针;第五个8字节分别是_vtable_index,取值是8和_access_flags,取值是1;第六个8字节分别是_intrinsic_id和_method_size,前者取值0,后者取值11;第七个8字节是_i2i_entry的地址,第八个8字节是_adapter的地址,跟inspect的结果一致;第九个8字节是_from_compiled_entry的地址,第十个8字节是_code的地址,空指针;第十一个8字节是_from_interpreted_entry的地址。

    再看ConstMethod的内存结构,用printas和mem命令查看,如下图:

    ConstMethod的内存大小是48字节,但是_constMethod_size是10*8=80字节,多出来的32字节就是内嵌到ConstMethod中的字节码,代码行号表,异常检查表等,这部分没有对应的属性所以sizeof没有统计这部分对应的内存。

    第一个8字节是_fingerprint, 第二个8字节是常量池指针_constants,第三个8字节是空指针_stackmap_data,第四个8字节是属性_constMethod_size,取值10,占后4字节,前面2字节是_flags,取值5,开始的2字节是填充的;第五个8字节分别是属性_method_idnum,取值4,_signature_index,取值37,_name_index,取值39,_code_size,取值9,给占2字节;第6个8字节分别是属性_method_idnum,取值4,_max_locals,取值1,_size_of_parameters,取值1,_max_stack取值2,至此ConstMethod的所有属性都有对应的内存区域,刚好48字节。

    ConstMethod的第十个8字节的起始地址是0x0000000016c539d0,下一个8字节的起始地址是0x0000000016c539d8,刚好是Method的起始地址,说明这两者在内存中是紧挨着的。

    7、Java vtable

         C++中的vtable只包含虚函数,非虚函数在编译期就已经解析出正确的方法调用了。Java vtable除了虚方法外还包含了其他的非虚方法。vtable中的一条记录用vtableEntry表示,该类在klassVtable.hpp中定义,该类只有一个属性Method* _method,只是对Method*做了简单包装而已,提供了相关的便利方法。访问vtable需要通过klassVtable类,该类也是在klassVtable.hpp中定义,提供了访问vtable中的方法的便利方法,如Method* method_at(int i),int index_of(Method* m)等,其实现都是基于vtable的内存起始地址和内存偏移来完成的。

          klassVtable包含三个属性,分别是_klass(该vtable所属的klass),_tableOffset(vtable在klass实例内存中的偏移量),_length(vtable的长度,即vtableEntry的条数,因为一个vtableEntry实例只包含一个Method*,其大小等于字段,所以vtable的长度跟vtable以字宽为单位的内存大小相同),_verify_count(用于记录vtable是否已经校验并初始化完成)如下图:

    继续以InstanceKlass中的示例代码说明,使用inspect命令查看jvmTest.A的内存大小为440字节,即55字宽,如下图:

    在440字节之后就是vtable了,其长度是9,用 mem 0x000000013f541240 56查看440字节之后的起始地址为0x000000013f5413f8,然后查看该地址之后9字宽的内容,如下图:

    可以在Code Viewer中查看这9个方法地址对应的方法实现,如下图:

    9个地址对应的方法如下:

    8、Java itable 

         Java itable是Java接口函数表,为了方便查找某个接口对应的方法实现。itable的结构比vtable复杂,除了记录方法地址外还得记录该方法所属的接口类klass地址,其中方法地址用itableMethodEntry表示,跟vtableEntry一样,只包含了一个Method* _method属性,方法所属的接口类klass地址用itableOffsetEntry表示,包含两个属性Klass* _interface(该方法所属的接口)和int      _offset(该接口下的第一个方法itableMethodEntry相对于所属Klass的偏移量)。itable的内存布局如下:

    这两者都是成对出现的。访问itable通过类klassItable实现,该类包含4个属性:_klass(itable所属的Klass),_table_offset(itable在所属Klass中的内存偏移量),_size_offset_table(itable中itableOffsetEntry的条数),_size_method_table(itable中itableMethodEntry的条数),如下图:

    通过mem查看itable 8个字段的内存,如下图:

     

    用Code Viewer可以查看各地址对应的接口类和方法,如下图: 

    其中 0x000000013f541470 减去 A的起始地址 0x000000013f541240,刚好就是偏移量0x230,即相对于vtable偏移15个字宽,vtable本身占9个字宽,加上x000000013f541470前面的6个字宽,刚好15个字宽。

    9、InstanceKlass特殊子类

    InstanceKlass一共有3个特殊子类,如下:

    • InstanceClassLoaderKlass,没有添加新的字段,增加了新的oop遍历方法,主要用于类加载器依赖遍历使用。
    • InstanceRefKlass,用于表示java/lang/ref/Reference的子类,这些类需要垃圾回收器特殊处理,因此改写了原有的oop_oop_iterate中用于垃圾回收的相关方法。
    • InstanceMirrorKlass,用于表示特殊的java.lang.Class类,java.lang.Class对应的OopDesc实例用于保存类的静态属性,因此他们的实例大小不同,需要特殊的方式来计算他们的大小以及属性遍历。Klass的属性_java_mirror就指向保存该类静态字段的OopDesc实例,可通过该属性访问类的静态字段。

       将jvmTest.A的静态属性改成4个int变量,如下图:

           通过HSDB可以查看_java_mirror属性,如下图:

            

    10、ArrayKlass

           ArrayKlass继承自Klass,是所有数组类的抽象基类,在Klass的基础上增加如下属性:

    • _dimension:int类型,表示数组的维度,记为n
    • _higher_dimension:Klass指针,表示对n+1维数组Klass的引用
    • _lower_dimension: Klass指针,表示对n-1维数组Klass的引用
    • _vtable_len: int类型, 虚函数表的长度
    • _component_mirror:oop, 数组元素对应的java/lang/Class的Oop

         该类的方法不多,主要是跟属性相关的方法,比较重要的就是两个分配数组内存的方法multi_allocate和allocate_arrayArray。

         ObjArrayKlass是ArrayKlass的子类,用于表示元素是类的数组或者多维数组,该类新增了两个属性:

    •  _element_klass:Klass指针,数组元素对应的Klass引用,如果是多维数组,则是对应数组元素的ObjArrayKlass的引用
    •  _bottom_klass:一维数组的类型,可以是InstanceKlass或者TypeArrayKlass

         该类主要增加了两个用于数组复制的重要方法,copy_array和do_copy,以及用于垃圾回收的oop_oop_iterate的相关方法。

          TypeArrayKlass是ArrayKlass的子类,用于表示元素是基本类型如int的数组,该类新增一个属性:

    •  _max_length:该数组的最大长度

           同ObjArrayKlass也增加了数组复制方法copy_array和用于垃圾回收的oop_oop_iterate的相关方法。

    测试用例如下:

    package jvmTest;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    
    class C{
        private static int a=1;
        private int b=2;
    
        public C(int b) {
            this.b = b;
        }
    }
    
    public class MainTest2 {
    
        public static void main(String[] args) {
            C[] c={new C(1)};
            C[][] c2={{new C(2),new C(3)},{new C(4)}};
    
            int[] i={1};
            int[][] i2={{1,2},{3,4,5}};
            while (true) {
                try {
                    System.out.println(getProcessID());
                    Thread.sleep(600 * 1000);
                } catch (Exception e) {
    
                }
            }
        }
    
        public static final int getProcessID() {
            RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            System.out.println(runtimeMXBean.getName());
            return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                    .intValue();
        }
    }

      因为数组不是一个单独的class,所以无法在Class Browser中查看,只能在Stack Memery中查看线程栈中4个数组变量所引用的ArrayKlass对应的OopDesc来查看,如下图:

    4个局部数组变量加上main方法的入参args,该参数是String数组,刚好是5个,这5个变量的顺序是按照线程栈入栈的顺序来的,最下面的是args,从下到上依次是c,c2,i,i2。用Inspect查看该地址对应的数据,i2如下图:

     

    c2的如下图:

     上图可知,基本类型数组用TypeArrayKlass表示,类数组或者多维数组用ObjArrayKlass表示,可将对应的TypeArrayKlass或者ObjArrayKlass展开查看对应的属性。

     

    展开全文
  • 当我们在写 Java 代码的时候,我们会面对着无数个...今天就来分析一下 HotSpot JVM 中的对象模型:oop-klass model。 1.OOP-Klass 模型概述 HotSpot JVM 并没有根据 Java 实例对象直接通过虚拟机映射到新建的 C++ 对

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

    1.OOP-Klass 模型概述

    HotSpot JVM 并没有根据 Java 实例对象直接通过虚拟机映射到新建的 C++ 对象,而是设计了一个 oop-klass 模型。oop 指的是 Ordinary Object Pointer(普通对象指针,指向被创建的对象,具体通过什么结构创建对象请向下看…),它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象;而 klass 则包含元数据和方法信息,用来描述 Java 类。

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

    注:以下C++代码参考自这篇文章

    2.OOP是什么?

    在 Java 程序运行的过程中,每创建一个新的实例对象,在 JVM 内部就会相应地创建一个对应类型的 oop 对象。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内部使用的特定对象的类型。比如 instanceOopDesc 表示类实例,arrayOopDesc 表示数组。也就是说,当我们用new创建一个Java对象实例的时候,JVM会创建一个 instanceOopDesc 对象来表示这个Java对象。同理,对于数组实例则是 arrayOopDesc。

    而各种 oop 类的共同基类为 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中。

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

    下图是一个对象头的示例:在这里插入图片描述

    3.Klass是什么?

    一个 Klass 对象代表一个类的元数据(与Class对象什么关系呢?向下看…)。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类型的父类:
    在这里插入图片描述
    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;
          ....
    

    Class 对象

    问题:Klass(InstanceKlass) 与 Class 对象有什么关系?

    我们知道,类数据这样的一个结构体实例也是一个对象,这个对象存在了方法区里。但是这个类数据对象即 InstanceKlass 是给VM内部用的,并不直接暴露给 Java 层。

    在 HotSpot VM 里,java.lang.Class 的实例被称为“Java mirror”,意思是它是 VM 内部用的 klass 对象的“镜像”;作用是把 InstanceKlass 对象包装了一层来暴露给 Java 层(开发者)使用,但主要用途是提供反射访问;创建时机时加载->连接->初始化 的初始化阶段。

    • 每个 Java 对象的对象头里,_klass 字段会指向一个VM内部用来记录类的元数据用的 InstanceKlass 对象

    • insanceKlass里有个 _java_mirror 字段(见上),指向该类所对应的Java镜像——java.lang.Class实例

    • 另外,HotSpot VM会给 Class 对象注入一个隐藏字段“klass”,用于指回到其对应的 InstanceKlass 对象。这样,klass与mirror之间就有双向引用,可以来回导航

    所以,当我们写 obj.getClass(),在 HotSpot VM 里实际上经过了两层间接引用才能找到最终的 Class 对象

    在这里插入图片描述

    PS:在Oracle JDK7之前,Oracle/Sun JDK 的 HotSpot VM 把Java类的静态变量存在 InstanceKlass 结构的末尾;从Oracle JDK7开始,为了配合 PermGen 移除的工作,Java 类的静态变量被挪到 Java mirror(Class对象)的末尾了。

    4.总结

    • JVM 在加载 class 时,会创建 instanceKlass,用来在JVM层表示该Java类,包括常量池、字段、方法等,存放在方法区;
    • 我们在Java代码中 new 一个对象时,JVM会创建一个instanceOopDesc实例来表示这个对象,存放在堆区,其引用,存放在栈区;这个对象中包含了两部分信息,对象头和元数据。对象头有一些运行时的数据,其中就包括跟多线程相关的锁的信息。元数据维护的是指针,指向的是对象所属的类的instanceKlass。
    • HotSpot 并不把 instanceKlass 暴露给 Java,而会另外创建对应的 instanceOopDesc 来表示 java.lang.Class 对象,并将后者称为前者的“Java镜像”,klass 持有指向 oop 引用(_java_mirror 便是该 instanceKlass对Class对象的引用);

    下面我们来分析一下,执行 new A() 的时候,JVM native 层里发生了什么?

    1. 首先,如果这个类没有被加载过,JVM 就会进行类的加载,并在 JVM 内部创建一个 instanceKlass 对象表示这个类的运行时元数据,
    2. new A() 时(执行 invokespecial A::),JVM 就会创建一个 instanceOopDesc 对象表示这个对象的实例,然后进行 Mark Word 的填充,将元数据指针指向 Klass 对象(KlassPointer),并填充实例变量。

    ==> 结合JVM分析以下代码执行过程:

    public class Test{
        public static void main(String[] args) {
          Person person = new Person();
          person.setName(“张三”);
        }
    }
    
    1. 编译并运行 Test.Class,随后被加载到JVM的元空间,在那存储着类的信息(包括类的名称、方法信息、字段信息…)。
    2. JVM找到 Test 的主函数入口(main),为 main 函数创建栈帧,开始执行 main 函数。
    3. main函数的第一条语句是 Person person = new Person(),就是让JVM创建一个 Person 对象,但是这时候方法区中没有 Person 类的信息,所以JVM马上加载 Person 类,把 Person 类的类型信息放到方法区中(元空间),即创建 instanceKlass 对象;同时,在堆上会自动构造一个镜像的 Class 对象提供反射访问(它和 instanceKlass 持有着双向引用)。
    4. 加载完 Person 类之后,JVM 会为一个新的 Person 实例分配内存, 然后调用构造函数初始化 person 实例,即在堆上创建一个 instanceOopDesc 实例,并返回该实例的指针OOP到栈空间对应线程。
    5. 当执行 person.setName("张三")的时候,JVM 首先根据 person 引用找到 person 对象;因为这个 person 实例持有着指向方法区的 Person 类的类型信息的引用(其中包含有方法表,java动态绑定的底层实现等),所以接下来,通过 KlassPointer 定位到方法区中 Person 类的类型信息的方法表,获得setName()函数的字节码的地址。
    6. 根据方法表中内容,在虚拟机栈为setName()函数创建栈帧,开始运行setName()函数。
    展开全文
  • Java对象模型-oop和klass

    千次阅读 2019-06-30 10:33:02
    oop-klass模型 Hotspot 虚拟机在内部使用两组类来表示Java的对象和类。 oop(ordinary object pointer),用来描述对象实例信息。 klass,用来描述 Java 类,是虚拟机内部Java类型结构的对等体 。 JVM内部定义了...
  • 什么是Klass& KlassKlass

    2021-07-17 00:24:12
    What is Klass & KlassKlass in JVM Hotspot Implementation?As far as I understood from the article Presenting the Perm Generation, Klass is the internal representation of a Java class (let's say A) ...
  • jvm源码分析之oop-klass对象模型

    千次阅读 2018-08-01 18:51:37
    概述 HotSpot是基于c++实现,而c++是...但HotSpot JVM并没有这么做,而是设计了一个OOP-Klass Model。这里的 OOP 指的是 Ordinary Object Pointer (普通对象指针),它用来表示对象的实例信息,看起来像个指针实际...
  •  Klass* _klass;  narrowKlass _compressed_klass;  } _metadata;  //.... } 在hotspot中对象指针称为oop(ordinary object pointer), 而oopDesc则是对象头的结构. 除了预想中的Klass(之所以叫kclass是因为...
  • 详细解释一下,定义这样一个类: class Main { } 那么当这个类所在的文件被加载,更准确地说,这个类被ClassLoader加载到JVM中的时候,Hotspot虚拟机会为这个类在虚拟机内部创建一个叫做Klass的数据结构: class ...
  • oop-klass 当C,C++和Delphi等程序被编译成二进制程序后,原来所定义的高级数据结构都不复存在了,当windows/linux等操作系统(宿主机)加载这些二进制程序时,是不会加载这些语言中所定义的高级数据结构的,宿主机压根就...
  • 一、oop-klass描叙 1、介绍 ​ 在JVM内存用到了oop-klass模型来描叙对应的类及对象:oop(ordinary object ponter,普通对象指针),其是用来描叙对象的实例信息。klass,其是JVM内部用来描叙类的信息的,例如Java类的...
  • 在《Java对象表示——Oop-Klass模型(一)》一文的最后讲到,为了实现Java方法调用的动态绑定,HotSpot使用了与C++虚函数类似的机制,同时为了避免每个对象都维护一个虚函数表,于是就设计了Klass类。 如下为HotSpot...
  • 现在使用广泛的hotspot虚拟机里,使用opp-klass模型,分两部分来表示Java类和对象,oop也就是ordinary object pointer,普通对象指针,用来标识对象的实例,klass则是用来描述类,标识里面的变量和方法等。...
  • java oop/klass对象模型

    2020-07-12 16:48:48
    klass klass主要描述 Java 类和 JVM内部C++类型的元信息和虚函数,这些元信息的实际值就保存在oop里面 // hotspot/src/share/vm/oops/oopsHierarchy.hpp … class Klass; // Klass继承体系的最高父类 class ...
  • OOP和klass的概念 HotSpot中采用了OOP-Klass模型,它是用来描述Java对象实例的一种模型 OOP或OOPS(Ordinary Object Pointer)指的是普通对象指针,主要职能是表示对象的实例数据,存储在堆里面 Klass用来描述...
  • 认清java 对象原理,对象结构如下图 java 对象结构声明是在 jvm 源码中的oop.hpp类中。...那么 Klass * 是一个Klass 结构的指针(也被称为元数据指针),这个指针指向的是哪呢? 我们知道 对象的...
  • jvm oop-klass对象模型

    千次阅读 2018-06-10 22:51:49
    一.oop-klass的层级关系 首先,从oopsHierarchy.hpp的源代码.看看jvm对象模型的层次结构. 引用Hotspot实战里面的一张图,oop各模块的组成: oop层级部分: klass层级部分: 二.oop层级部分: 看完了jvm整个...
  • JVM中OOP-KLASS模型

    2020-04-01 23:23:11
    对象的创建与OOP-Klass模型 目录 1、JVM中OOP-KLASS模型 正文 1、JVM中OOP-KLASS模型 在JVM中,使用了OOP-KLASS模型来表示java对象,即: 1.jvm在加载class时,会创建instanceKlass,表示其元数据,包括...
  • 原标题:Java知识进阶-程序员未接触的知识点之对象模型Klass Word-知识铺Java对象模型 - Klass Word一、OOP-Klass 1. Klass Word: 是java对象模型中的头部信息,用来描述java类,和虚拟机中的Java类型结构相对应着...
  • Klass Pointer:Class对象的类型指针,Jdk1.8默认开启指针压缩后为4字节,关闭指针压缩(-XX:-UseCompressedOops)后,长度为8字节。其指向的位置是对象对应的Class对象(其对应的元数据对象)的内存地址。 对象...
  • HotSpot Oop/Klass 模型

    千次阅读 2017-12-22 15:39:14
    HotSpot JVM 没有将Java对象直接通过虚拟机映射到 C++ 对象,而是设计了一个 Oop/Klass 模型,其中 oop 为 Ordinary Object Pointer,用来表示对象的实例信息;klass 用来表示对象元数据信息,不是单指 Class 类的...
  • var klass = require ( 'klassjs' ) ; var Robot = klass ( null , { _construct : function ( v ) { this . weapon = v ; } , fireWeapon : function ( ) { return this . weapon ; } } ) ; var Cyborg = ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,780
精华内容 6,312
关键字:

klass