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

其中各类的说明如下:

类名 说明
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)

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

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

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

 

 总结起来就是:

instanceOopDesc包含如下信息:
1. 对象头,也叫Mark Word,主要存储对象运行时记录信息,如hashcode, GC分代年龄,锁状态标志,线程ID,时间戳等;
2. 元数据指针class pointer,即指向方法区的instanceKlass实例 (虚拟机通过这个指针来群定这个对象是哪个类的实例。)
3. 实例数据 instance data;
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)

 

posted @ 2020-12-17 18:06  蒙恬括  阅读(1049)  评论(0编辑  收藏  举报