跳至主要內容

JVM (一) oop-klass 模型--HSDB 使用验证

安图新大约 5 分钟

JVM (一) oop-klass 模型--HSDB 使用验证

一:oop-kclass 模型

思考:我们平时写的 java 类编译成.class 文件,JVM 加载.class 文件,那么加载.class 文件之后在 JVM 中就是 oop-kclass(C++)模型形式存在的。

JVM 内部基于 oop-klass 模型描述一个 java 类以及其实例(对象),java 类元信息用 klass 描述,对象用 oop 来描述。

oop: ordinary object pointer,也即普通对象指针.oop 成员众多

 

这里有个问题,不是说 xxxoop 吗?怎么类图上怎么就是 xxxppDesc 了?
原因在于 c++描述 oop 的层级关系时在 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;

View Code

其中各类的说明如下:

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

上面的一个类就是描述我们平常创建的 Java 实例:instanceOopDesc,对象在内存中的布局就是通过它来表示的,关于这一点可以参考之前写的关于

《关于 Object = new Object();的美团六问》:https://blog.csdn.net/A7_A8_A9/article/details/105730007 的文章。

里面有个关于对象的内存图:

 
 

总结起来就是:

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

5:对齐填充(有时候不需要)

klass

1、 klass 提供一个与 java 类对等的 c++类型描述;
2、 klass 提供虚拟机内部的函数分发机制;
3、 jvm 在加载 class 时,会创建 instanceKlass,表示其元数据,包括常量池、字段、方法等,存放在方法区;instanceKlass 是 jvm 中的数据结构;
在 new 一个对象时,jvm 创建 instanceOopDesc,来表示这个对象,存放在堆区,其引用,存放在栈区;它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象;instanceOopDesc 对应 java 中的对象实例; 4、 klass 是在方法区的;
5、 普通的 Java 类和数组用不同的 kclass 来表示,后面使用 HSDB 工具可以看到 JVM 运行时常量池中的 Java 实例存在的形式;

 
 

之间 oop-klass 之间的联系:

 
 

handle

除了 oop,kclass 外在 JVM 中还有一个东西 handle:

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

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

三者的关系如下:

 
 

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

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

二:使用 HSDB 查看 JVM 中运行时的内容

HSDB 是 JDK 自带的分析 JVM 的利器,运行方式在 jdk 安装目录/lib 下有一个 **sa-jdi.jar ,**运行命令:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

注意项目上用的 jdk 和环境配置的 jdk 要一致,不然就要指定上面那个 java 是哪个 jdk 版本的。

运行完会打开如下界面:

 
 

用的测试例子如下:已经在运行

 
 

我们查看这个进程的 id:

 
 

得到 id 之后,操作如下

 
 
 
 

把刚才的 id cp 进去点击 ok

 
 

等待一会出现当前进程的线程栈:

 
 

选中 main 线程,点击 Tool 下的 class browser

 
 

看到当前线程所涉及到的类,

 
 

@后面是地址,我们 cp 下 TestLog 的地址:

 
 

填入下面的位置回车:我们就可以看到在 JVM 中我们定义的 TestLog 其实是用 InstanceKlass 来表示的。

我们看到 TestLog 的 Class 在 JVM 中也是用 Oop 表示的。

1:HotSpot 并不把 instanceKlass 暴露给 Java,而会另外创建对应的 instanceOopDesc 来表示 java.lang.Class 对象,并将后者称为前者的“Java 镜像”就是图中看到的'_java_mirror'属性。

2:InstanceKlass 持有指向 oop 引用(_java_mirror 便是该 instanceKlass 对 Class 对象的引用)。

3:new 操作返回的是对 instanceOopDesc 类型的引用(在栈中),而 instanceOopDesc 指针(堆中)指向 instanceKlass(方法区),而 instanceKlass 指向了对应的类型的 Class 实例的 instanceOopDesc(就是那个_java_mirror)

 
 
上次编辑于:
贡献者: Andy