博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenJDK9 Hotspot :oops,klass 与 handle
阅读量:6532 次
发布时间:2019-06-24

本文共 7527 字,大约阅读时间需要 25 分钟。

前言

Klass

A Klass provides:

  • language level class object (method dictionary etc.)

  • provide vm dispatch behavior for the object

both functions are combined into one c++ class

说白了 Klass 就是 Java 中的类在 JVM 中的内部表示

类层次结构

MetaspaceObj    Metadata        Klass

MetaspaceObj 类重载了 new 运算符来保证 Klass 分配在 metaspace(元空间,老版 JVM 叫持久代),

属性

Klass 类包含各种属性,详细描述 Java 类的类名,内存布局(大小),方法表,父子类关系 .etc

类名

Symbol* _name

父类

Klass* _super

子类

类可以有多个子类,通过 next-sibling 方式保存父子类关系

Klass* _subklassKlass* _next_sibling;

类加载器

Java 中类名和类加载器唯一标识了一个类,由同一个类加载器加载的类通过 _next_link 连接起来

oop

oop 是 ordinary object pointer 的缩写,oop 是 Java 对象的 JVM 内部表示. 如果没有打开CHECK_UNHANDLED_OOPS 预编译开关 oop 是指向 oopDesc 的指针,否则它是 oopDesc 指针的包装类

// oopsHierarchy.hpp#ifndef CHECK_UNHANDLED_OOPStypedef class oopDesc*                            oop;typedef class instanceOopDesc*            instanceOop;typedef class arrayOopDesc*                  arrayOop;typedef class objArrayOopDesc*            objArrayOop;typedef class typeArrayOopDesc*          typeArrayOop; # elseclass oop {    oopDesc* _o    ...}#end

简单起见我们假定 CHECK_UNHANDLED_OOPS 为关闭状态,直接讨论 oopDesc 及其子类,这里先附上 oop 类层次图

oop

oopDesc

oopDesc 类是虚拟机核心数据结构,字段和方法比较多,很多字段和方法与 JVM 运行期核心算法有关,所以一时看不懂也没啥,后续看到核心算法时再回过头来就会有 柳暗花明 的感觉

出于性能优化的考虑,oopDesc 类大量的 "小" 方法都被声明成 static inline

对象内存布局

对象内存布局分为 header(头部)和 fields(实例字段),header 又由 markOop(也是一个 oop)和 metadata 组成,metadata 存储者对象所属的类(KClass),这里使用了 union 来声明 metadata 是为了在 64 位机器上对对象指针进行压缩,64 位机器上指针类型占用 8 个字节,这种优化很多教材和文章都提到过

class oopDesc {private:    volatile markOop _mark;    union _metadata {        Klass* _klass;        narrowKlass _compressed_klass;    } _metadata;}

oopDesc 类的 field_base 方法用于计算 类实例字段 的地址,offset 是偏移量

// oop.inline.hppinline void* field_base(int offset) const {    return (void*)&((char*)this)[offset];}

因此可以推测,类实例字段存放在 oopDesc 中,紧跟着 oopDesc 本身占用的内存空间之后,这一点还可以从 header_size 方法证实,该方法返回对象头部(oopDesc 类)大小

// oop.inline.hpp// size of object header, aligned to platform wordSizestatic int header_size() { return sizeof(oopDesc)/HeapWordSize; }

instanceOopDesc

instanceOopDesc 是 oopDesc 的子类,和 oopDesc 基本没差,只是定义了一些 static 工具方法

// instanceOop.hpp// An instanceOop is an instance of a Java Class// Evaluating "new HashTable()" will create an instanceOop.class instanceOopDesc : public oopDesc {    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 是所有数组对象的基类,它的对象头除了 oopDesc 中声明的 markOop, Klass* 之外多了个 length 用于描述数组元素个数,聪明的你肯定猜到了,和实例字段一样,数组中的元素挨个存储在 头部 之后

length 和 set_length 方法用于获取和设置数组长度

int length() const {    return *(int*)(((intptr_t)this) + length_offset_in_bytes());  }  void set_length(int length) {    *(int*)(((intptr_t)this) + length_offset_in_bytes()) = length;  }

length_offset_in_bytes 用于计算 length 字段在对象内存布局中的偏移量,代码中的注释说明了为啥要这么干

// 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);  }

objArrayOopDesc

typeArrayOopDesc

jhsdb

编译 openjdk 代码之后,在 jdk/bin 目录下有个实用程序 jhsdb,可以用来查看 hotspot 内部信息

我们写个简单的 Main 类,该类有两个字段 field1 和 field2

public class Main {    private int field1;    private int field2;    public static void main(String[] args) throws Exception {        Main main = new Main();        while (true) {            Thread.currentThread().sleep(3000);        }    }}

使用编译出来的 openjdk 里面的 java 程序运行该类,记下进程号

# java Main &[1] 11780

clhsdb

启动 jhsdb,通过 clhsdb 选项选择使用 command line debugger

# jhsdb clhsdb# hsdb>

通过 help 命令可以查看支持的 命令列表

# hsdb> help assert true | false  attach pid | exec core  buildreplayjars [ all | app | boot ]  | [ prefix ]  detach  dis address [length]  disassemble address  dumpcfg { -a | id }  dumpcodecache  dumpideal { -a | id }  dumpilt { -a | id }  dumpreplaydata { 
| -a |
} echo [ true | false ] ...

我们使用 attach 命令 attach 到刚才那个 11780 进程(注意,必须使用 root 帐号执行该操作)

#hsdb> attach 11780Attaching to process 11780, please wait...hsdb>

可能会有一些关于 js engine 警告,先忽略,我们先查看一下 Main 符号定义

hsdb>symboltable Mainsun.jvm.hotspot.oops.Symbol@0x00007fe7c446a720

使用 jstack 查看线程

#hsdb> jstackThread 11785: (state = BLOCKED) - java.lang.Thread.sleep(long) @bci=0 (Compiled frame; information may be imprecise) - Main.main(java.lang.String[]) @bci=15, line=10 (Interpreted frame)

使用 where 查看 stack trace

#hsdb> where 11785Thread 11785 Address: 0x00007fe7c4019000Java Stack Trace for mainThread state = BLOCKED - public static native void sleep(long) @0x00007fe7b4232bc8 @bci = 0, pc = 0x00007fe7bcadedb8 (Compiled; information may be imprecise) - public static void main(java.lang.String[]) @0x00007fe7b471fb00 @bci = 15, line = 10, pc = 0x00007fe7b500b4a3 (Interpreted)

使用 print 查看 0x00007fe7b471fb00 代码

public static void main(java.lang.String[]) @0x00007fe7b471fb00Holder Classpublic class Main @0x00000007c0085030Checked Exception(s)public class java.lang.Exception @0x00000007c00030d0Bytecodeline bci   bytecode 8   0   new #2 [Class Main] 8   3   dup 8   4   invokespecial #3 [Method void 
()] of public class Main @0x00000007c0085030 8 7 astore_1 10 8 invokestatic #4 [Method java.lang.Thread currentThread()] of public class java.lang.Thread @0x00000007c00068b0 10 11 pop 10 12 ldc2_w #5
10 15 invokestatic #7 [Method void sleep(long)] of public class java.lang.Thread @0x00000007c00068b0 10 18 goto 8 Constant PoolConstant Pool of [public class Main @0x00000007c0085030] @0x00007fe7b471f860

hsdb

启动 jhsdb,hsdb 选项选择使用 ui debugger,界面比较粗糙~~~

# jhsdb hsdb

选择 File 菜单中的 Attach to hotspot process,输入 PID 11780

图片描述

选择 Tools 菜单中的 Object Histogram,打开 heap 中的 object 列表,在搜索框中输入 Main 并查找

图片描述

双击 Main item,点击底部的 inspect 按钮查看 Main 对象对应 oop,可以很清楚的看到对象布局

图片描述

Handle

可以将 Handle 类理解成访问对象的一个 "句柄",handles.hpp 文件头部的注释说明了为什么要增加这一层间接访问:

In order to preserve oops during garbage collection, they should beallocated and passed around via Handles within the VM. A handle issimply an extra indirection allocated in a thread local handle area

垃圾回收时对象可能被移动(对象地址发生改变),通过 handle 访问对象可以对使用者屏蔽垃圾回收细节

使用 Handle 的示例:

oop obj = ...;Handle h1(obj);              // allocate new handleHandle h2(thread, obj);      // faster allocation when current thread is knownHandle h3;                   // declare handle only, no allocation occurs..h3 = h1;                     // make h3 refer to same indirection as h1oop obj2 = h2();             // get handle valueh1->print();

Handle 实现和 "智能指针" 类似,主要是通过运算符重载:

// handles.hpp// General accessoop     operator () () const                   { return obj(); }oop     operator -> () const                   { return non_null_obj(); }bool    operator == (oop o) const              { return obj() == o; }bool    operator == (const Handle& h) const    { return obj() == h.obj(); }

总结

转载地址:http://wzhbo.baihongyu.com/

你可能感兴趣的文章
Linux MySQL 储存中文失败简单解决办法
查看>>
求最大值及其下标
查看>>
洛谷——P1330 封锁阳光大学
查看>>
css选择器
查看>>
zabbix-agent配置文件说明
查看>>
linux系统配置之bash shell的配置(centos)
查看>>
linux C 9*9
查看>>
hdu 1695: GCD 【莫比乌斯反演】
查看>>
python的string操作总结
查看>>
如何把word中的图片怎么导出来呢?
查看>>
Python连接Arduino的方法
查看>>
CMD指令大全
查看>>
十五天精通WCF——第二天 告别烦恼的config配置
查看>>
Qt多线程学习:创建多线程
查看>>
设计模式学习---UML常见关系的实现
查看>>
图解openssl实现私有CA
查看>>
BZOJ2213 : [Poi2011]Difference
查看>>
c++ Constructor FAQ 继续
查看>>
事务之六:spring 嵌套事务
查看>>
C#:路径
查看>>