JVM层面的JAVA类和实例(Klass-OOP)

一、Klass-OOP概述

1.1 基本概念

在HotSpot中,

  • Klass 来描述 Java 类,不同的Java类由不同的Klass 实例对象定义;
  • oop(ordinary object pointer, 普通对象指针) 定义为一块内存区域;
  • OopDesc 来描述这块内存的内容,即描述Java实例对象,不同的Java实例对象对应不同的OopDesc对象。

JVM 使用 C++ 编写的,C++ 定义了2个类 KlassOopDesc 分别用来描述Java中类和对象, 所以Java中的类和对象对于C++而言,均是 C++ 创建的对象。

类比注解和元注解。C++定义的Java类产生的对象就是元注解;Java代码产生的对象就是注解。

1、 JAVA代码通过 new Object() 创建对象,图示{1};

2、JVM会加载对应的 class,解析生成 Klass 对象,图示{2}、{3};

3、JVM更加加载的 Klass 对象,创建 OopDesc 对象,图示{4}。

注意:

  • Java的每个类,在JVM中都有一个对应的Klass类实例与之对应,存储类的元信息如:常量池、属性信息、方法信息......
  • Klass对象实例由JVM自己产生,放在JVM元空间;
  • OopDesc对象实例由Java代码定义产生,放在JVM堆内存;
  • 简言之,JVM创建的对象统一放在JVM元空间,java代码创建的对象统一放在堆内存;

1.2 具体实现

为了更加精细化管理Java中的类和对象,JVM层面也定义很多了Klass的子类和OopDesc的子类。

大的维度为普通类 (Instance)和数组类(Array):

  • InstanceKlassInstanceOopDesc 分别用于普通的Java 类、普通的Java对象;
  • ArrayKlassArrayOopDesc 分别用于数组类型的Java 类、数组类型的Java对象;

对于普通的Java 类InstanceKlass 而言,分别为Java中3个特殊的类定义了子类:

  • InstanceMirrorKlass java.lang.Class
  • InstanceRefKlass java.lang.ref.Reference
  • InstanceClassLoaderKlass java.lang.ClassLoader

二、代码简析

2.1、InstanceKlass代码简析

InstanceKlass 里面的变量基本和字节码(更多详情参见字节码(二))中的内容一致,本质就是把字节码加载并解析到JVM中。

cpp 复制代码
class InstanceKlass: public Klass {
//......
protected:
  // 保存该类使用的所有注解
  Annotations*       _annotations;
  // 数组元素为该类的数组Klass指针,即数组内部元素的 Klass 指向数组的 klass
  Klass*             _array_klasses;
  // 常量池信息
  ConstantPool*     _constants;
  // java 内部类信息
  Array<jushort>*   _inner_classes;
 
  // 举例:java中Object类在字节码中的名字: "[Ljava/lang/Object;"
  Symbol*           _array_name;
 
  // java 类实例成员占用内存大小,以字(word)为单位;在为当前类表示的Java类所创建的对象(使用oop表示)分配内存时使用
  int               _nonstatic_field_size;
  // java 类成员占用内存大小,以字(word)为单位;在为当前类表示的Java类所创建的java.lang.Class对象(使用oop表示)分配内存时使用
  int               _static_field_size;    
  
  // Constant pool index to the utf8 entry of the Generic signature,
  // or 0 if none.
  // 类的签名在常量池中的索引
  u2                _generic_signature_index;
  // Constant pool index to the utf8 entry for the name of source file
  // containing this klass, 0 if not specified.
  // 类的源文件名在常量池中的索引
  u2                _source_file_name_index;

  // 此类包含的静态引用类型字段的数量
  u2                _static_oop_field_count;
  // java 字段总数量
  u2                _java_fields_count;    

  // 非静态的oop map block需要占用的内存大小,以字为单位,GC时快速标记对象可达,快速构造引用链
  int               _nonstatic_oop_map_size;
  OopMapBlock*      _nonstatic_oop_map
 

  u2                _minor_version;  // minor version number of class file
  u2                _major_version;  // major version number of class file

  // 执行此类初始化的Thread指针
  Thread*           _init_thread;    // Pointer to current thread doing initialization (to handle recusive initialization)
  
  // Java虚函数表(vtable)所占用的内存大小,以字为单位
  int               _vtable_len;     // length of Java vtable (in words)
  // Java接口函数表(itable)所占用的内存大小,以字为单位
  int               _itable_len;     // length of Java itable (in words)

  // 用于存放java类所有方法
  OopMapCache*      volatile _oop_map_cache;   // OopMapCache for all methods in the klass (allocated lazily)

  // JNI方法操作属性
  JNIid*            _jni_ids;              // First JNI identifier for static fields in this class
  // JNI方法操作方法
  jmethodID*        _methods_jmethod_ids;  // jmethodIDs corresponding to method_idnum, or NULL if none

  // 依赖的本地方法,以根据其_next属性获取下一个nmethod
  nmethodBucket*    _dependencies;         // list of dependent nmethods

  // 栈上替换的本地方法链表的头元素
  nmethod*          _osr_nmethods_head;    // Head of list of on-stack replacement nmethods for this class

 
  // 类的状态
  // Class states are defined as ClassState (see above)
  // allocated(已分配内存)
  // loaded(从class文件读取加载到内存中)
  // linked(已经成功链接和校验)
  // being_initialized(正在初始化)
  // fully_initialized(已经完成初始化)
  // initialization_error(初始化异常)
  // Place the _init_state here to utilize the unused 2-byte after
  // _idnum_allocated_count.
  u1                _init_state;                    // state of class

  // 引用类型: 强引用、软、弱、虚 、FinalReference  
  u1                _reference_type;                // reference type
 

  // Method array. 保存方法的指针数组
  Array<Method*>*   _methods;
  
  // Default Method Array, concrete methods inherited from interfaces
  Array<Method*>*   _default_methods; // 接口继承的方法
  // Interface (Klass*s) this class declares locally to implement.
  Array<Klass*>*    _local_interfaces; // 保存直接实现的接口
  // Interface (Klass*s) this class implements transitively.
  Array<Klass*>*    _transitive_interfaces; // 保存全量实现的接口(直接+间接)

  // Int array containing the vtable_indices for default_methods
  // offset matches _default_methods offset
  Array<int>*       _default_vtable_indices; // 默认方法在虚函数表中的索引
 
  // Instance and static variable information, starts with 6-tuples of shorts
  // [access, name index, sig index, initval index, low_offset, high_offset]
  // for all fields, followed by the generic signature data at the end of
  // the array. Only fields with generic signature attributes have the generic
  // signature data set in the array. The fields array looks like following:
  //
  // f1: [access, name index, sig index, initial value index, low_offset, high_offset]
  // f2: [access, name index, sig index, initial value index, low_offset, high_offset]
  //      ...
  // fn: [access, name index, sig index, initial value index, low_offset, high_offset]
  //     [generic signature index]
  //     [generic signature index]
  //     ...
  Array<u2>*        _fields; // 类的字段属性,每个字段的6个属性 如:访问控制、属性名、初始值,内存中的偏移量;此外,保存完所有属性之后还可能会保存泛型签名信息。
  

   // _java_mirror ,指向java层面的class对象(类.class) 
   oop             _java_mirror
	...

  // 解析一个类时计算内存大小
  int size(int     vtable_len,
           int     itable_len,
           int     nonstatic_oop_map_size,
           bool    isinterf,
           bool    is_anonymous)
  {
	  return align_object_size(header_size()    +  // InstanceKlass类本身占用的内存大小
	   align_object_offset(vtable_length) +
	   align_object_offset(itable_length) +
	   //    [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
	   (
			  (is_interface || is_anonymous) ?
			  align_object_offset(nonstatic_oop_map_size) :
			  nonstatic_oop_map_size
	   ) +
	   //    [EMBEDDED implementor of the interface] only exist for interface
	   (
			   is_interface ? (int)sizeof(Klass*)/HeapWordSize : 0
	   ) +
	   //    [EMBEDDED host klass        ] only exist for an anonymous class (JSR 292 enabled)
	   (
			   is_anonymous ? (int)sizeof(Klass*)/HeapWordSize : 0)
	   );
  }

  // Sizing (in words) 计算 InstanceKlass类所需内存
  static int header_size(){ 
    return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); // 以HeapWordSize为单位,64位一个字为8字节,所以值为8 
  }
}

2.2、InstanceRefKlass代码简析

java.lang.ref.Reference的子类需要使用InstanceRefKlass 类来表示,因为这些类需要垃圾回收器特殊处理,具体详见细说Java 引用(强、软、弱、虚)和 GC 流程(一)

cpp 复制代码
class InstanceRefKlass : public InstanceKlass{
  // reference type, 引用类型: 软、弱、虚 、FinalReference ,该字段继承自InstanceKlass
  //u1                _reference_type;             
}

enum ReferenceType {
  REF_NONE,      // Regular class 强引用
  
  // 比如:用户自定义的 Reference 子类
  REF_OTHER,     // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below
  
  REF_SOFT,      // Subclass of java/lang/ref/SoftReference
  REF_WEAK,      // Subclass of java/lang/ref/WeakReference
  REF_FINAL,     // Subclass of java/lang/ref/FinalReference
  REF_PHANTOM    // Subclass of java/lang/ref/PhantomReference
};

2.3、InstanceMirrorKlass代码简析

cpp 复制代码
class InstanceMirrorKlass: public InstanceKlass{
	// 增加了一个静态属性_offset_of_static_fields,用来描述静态字段的起始偏移量
	static int _offset_of_static_fields;
}

InstanceMirrorKlass对象用于表示 java.lang.Class类,那java.lang.Class特殊在哪里,需要定制InstanceKlass?

  • JAVA 类中变量有类变量(static)和实例变量,实例变量存在对象实例(oop)中,类变量存储在 java.lang.Class对象中,如下图所示:


重点来了,Class自身也是个java类,Class自己也有类变量,那Class自己的类变量放哪里? 这很像【鸡生蛋,蛋生鸡】的问题,得有个终止条件啊?Java层面解决不了了,只能借助 C++ 之前定义的 InstanceKlass 了来存放了类变量了

那Class的实例变量存哪里呢?貌似也没有Class实例,也只能放在InstanceKlass 了;这样一来,Class的类变量和实例变量均在InstanceKlass 里了,为了好区分,需要新增字段_offset_of_static_fields了,InstanceMirrorKlass也就应运而生了。

2.3.1 InstanceMirrorKlass实例对象放哪里?

文章一开始,我们将Klass对象放在了JVM元空间,Java层面创建的对象放在JVM堆内存,而普通类的class对象Java层面的,同样会放在JVM堆内存;

对于 java.lang.Class的Klass对象即InstanceMirrorKlass实例对象,我们需要特殊对待,把它放在JVM堆内存。

小节:JAVA中,类的class对象和静态字段都存放在JVM堆内存中。

2.4、 InstanceClassLoaderKlass代码解析

cpp 复制代码
class InstanceClassLoaderKlass: public InstanceKlass{
	// 增加了新的oop遍历方法,主要用于类加载器依赖遍历使用。
}

2.5、OopDesc 代码简析

cpp 复制代码
class OopDesc {
 private:
  volatile markoop_mark;// 标记字
  union _metadata {
    Klass*      _klass; // klass 指针;
    narrowKlass _compressed_klass; // klass 压缩指针;
  } _metadata;
}

这代码熟悉吗,这不是JAVA对象的头部吗,markWord 和 类型指针(java 指针细节详情参见Java 引用是4个字节还是8个字节?

三、Klass-OOP 内存分布

经过前面学习,相信下图你已经可以完全理解了。

类加载过程

1、通过类的全限定名(包名+类名)获取存储该类的class文件;

2、解析成运行时数据,即instanceKlass实例,存放在元空间;

3、在堆区生成该类的Class对象,即instanceMirrorKlass实例;

可以看到,通过Object.class我们需要经过 {1}、{2} 才能得到结果。

相关推荐
奋进的芋圆18 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin19 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model200519 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉19 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国19 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_9418824820 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈20 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_9920 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹20 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理
专注_每天进步一点点20 小时前
【java开发】写接口文档的札记
java·开发语言