Android内存结构解析——klass内存分布

作者: 谁动了我的代码 | 来源:发表于2022-11-05 20:34 被阅读0次

    .class文件

    .class文件是编译.java文件生成的,即所谓的字节码文件。理论上说,不论是什么编程语言,只要经过编译器编译生成的文件符合class文件规范,都可以在JVM上正常运行。这也是如Groovy、Kotlin、Scala……这些语言能够在JVM上运行的本质原因。

    想深入学习JVM,字节码文件是避不开的一个知识点。这块知识点相对JVM其他的知识点来说算比较简单的,想深入学习JVM的同学建议补下这块知识。如果你是在看不懂也没关系,我会在VIP课中深入讲解,手动模拟类加载器解析class文件的过程。

    instanceKlass类

    这是一个C++类,是Java类在JVM中的存在形式,是.class文件经过类加载器子系统加载后生成的。注意:这个类只是Java类在JVM中的存在形式,并不是Class对象。看核心代码

    instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
    
                                                        ClassLoaderData* loader_data,
    
                                                        Handle protection_domain,
    
                                                        KlassHandle host_klass,
    
                                                        GrowableArray<Handle>* cp_patches,
    
                                                        TempNewSymbol& parsed_name,
    
                                                        bool verify,
    
                                                        TRAPS) {
    
      ……
    
        // We can now create the basic Klass* for this klass
    
        _klass = InstanceKlass::allocate_instance_klass(loader_data,
    
                                                        vtable_size,
    
                                                        itable_size,
    
                                                        info.static_field_size,
    
                                                        total_oop_map_size2,
    
                                                        rt,
    
                                                        access_flags,
    
                                                        name,
    
                                                        super_klass(),
    
                                                        !host_klass.is_null(),
    
                                                        CHECK_(nullHandle));
    
        instanceKlassHandle this_klass (THREAD, _klass);
    
    ……     
    

    instanceKlass是Klass类的子类,除了完全继承了Klass类的非私有属性外,自己又定义了很多属性用来描述Java类的信息,我们来看看这些属性(标成蓝色的是比较常见的):

    • _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:类的成员属性

    Klass类

    Klass类是Klass体系的基类,看下继承关系


    从类名称上也能看出来,Klass相关类与元空间之间的关系。大多数情况下,Klass的子类是存储在元空间中的。正常情况下在C++中new一个对象,默认是在操作系统内存中的,JVM是如果实现在元空间中分配的呢?答案是重载C++的操作符new,是不是这样呢?看代码

    void* MetaspaceObj::operator new(size_t size, ClassLoaderData* loader_data,
    
                                     size_t word_size, bool read_only,
    
                                     MetaspaceObj::Type type, TRAPS) throw() {
    
      // Klass has it's own operator new
    
      return Metaspace::allocate(loader_data, word_size, read_only,
    
                                 type, CHECK_NULL);
    
    }
    
    

    Klass类有很多属性,我介绍几个重要的吧:

    _layout_helper:如果这个Klass既不是instance也不是array,值为0,如果这个Klass是instance,值为对象的大小,如果这个Klass是array,值为负数。因为数组类型是运行时动态创建的,编译期无法知晓大小。

    _java_mirror:这个Klass对于的镜像类的指针

    Class对象

    JVM并没有将描述Java类元信息的instanceKlass直接暴露给Java程序使用,而是又抽象了一层,即所谓的镜像类:instanceMirrorKlass。jdk8以后,类的静态属性也由存储在instanceKlass实例中转为存放在镜像类的实例中。详情见源码

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

    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对象是分配在堆上的。


    以上就是Android虚拟机中内存结构klass内存解析;这是Android开发中的基础必备;更多Android进阶核心技术学习《Android核心进阶技术》可私:“手册”参与获取免费方式。

    结语

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

    相关文章

      网友评论

        本文标题:Android内存结构解析——klass内存分布

        本文链接:https://www.haomeiwen.com/subject/fhxmtdtx.html