《深入理解Java虚拟机》:JVM高级特性与最佳实践(第3 版)4.3.1 JHSDB:基于服务性代理的调试工具 (笔记随录)

根据这一章节的代码,给出jdk8(默认Parallel Scavenge + Parallel Old垃圾收集器)的JHSDB截图。

写这个随笔,主要方便自己的github笔记有图片(不想直接上传图片到github,那样下次clone太慢了)

1. java代码如下(来自书籍)

/**
* staticObj、instanceObj、localObj存放在哪里?
*/
public class JHSDB_TestCase {
  static class Test {
    static ObjectHolder staticObj = new ObjectHolder();
    ObjectHolder instanceObj = new ObjectHolder();
    void foo() {
      ObjectHolder localObj = new ObjectHolder();
      System.out.println("done"); // 这里设一个断点
    }
  }
  private static class ObjectHolder {}
  public static void main(String[] args) {
    Test test = new JHSDB_TestCase.Test();
    test.foo();
  }
}

上述代码,理论上:

  • staticObj随着Test的类型信息存放在方法区
  • instanceObj随着Test的对象实例存放在Java堆
  • localObject则是存放在foo()方法栈帧的局部变量表中

下面使用JHSDB工具做验证

2. 根据书本建议,启动JVM时指定参数如下:

-Xmx10m -XX:+UseSerialGC -XX:-UseCompressedOops

3. JPS查看LVMID(通常==PID)

这里 1587 就是我要的

$ jps                  
2216 Launcher
2218 JHSDB_TestCase
2219 Jps
1247

4. JHSDB图形化模式,附加到java进程

$ jhsdb hsdb --pid 2218

输入上述指令后,出现图形化界面如下:

5. 验证对象所在区域

使用scanoops命令在Java堆的新生代(从Eden起始地址到To Survivor结束地址)范围内查找ObjectHolder的实例。

下述指令在新出现的Console窗口里输入,并得到结果(和网上一篇CSDN的博客一样。我这里也是获取不到对象)

scanoops 0x000000010dc00000 0x000000010df00000 JHSDB_TestCase$ObjectHolder
No such type.

只好用另一个方法,查询 对象所在之处

双击 "gc.JHSDB_TestCase$ObjectHolder"

上图,验证了一般情况下新对象在Eden中创建的分配规则。

使用Tools->Inspector功能确认一下这三个地址中存放的对象

(下述文字是该书的原话)
Inspector为我们展示了对象头和指向对象元数据的指针,里面包括了Java类型的名字、继承关系、实现接口关系,
字段信息、方法信息、运行时常量池的指针、内嵌的虚方法表(vtable)以及接口方法表(itable)等
由于我们的确没有在ObjectHolder上定义过任何字段,所以图中并没有看到任何实例字段数据。

接下来要根据堆中对象实例地址找出引用它们的指针,原本JHSDB的Tools菜单中有Compute
Reverse Ptrs来完成这个功能,但在笔者的运行环境中一点击它就出现Swing的界面异常,看后台日志是
报了个空指针,这个问题只是界面层的异常,跟虚拟机关系不大,所以笔者(书籍的作者)没有继续去深究,改为使
用命令来做也很简单,先拿第一个对象来试试看:

revptrs 0x000000010dc532b0
null
Oop for java/lang/Class @ 0x000000010dc519a8
hsdb> 

根据查询到的堆中对象的地址,回去查指向堆中对象的指针(发现是个java.lang.Class类型的对象实例staticObj)

从《Java虚拟机规范》所定义的概念模型来看,所有Class相关的信息都应该存放在方法区之中,
但方法区该如何实现,《Java虚拟机规范》并未做出规定,这就成了一件允许不同虚拟机自己灵活把
握的事情。JDK 7及其以后版本的HotSpot虚拟机选择把静态变量与类型在Java语言一端的映射Class对
象存放在一起,存储于Java堆之中,从我们的实验中也明确验证了这一点

这次找到一个类型为JHSDB_TestCase$Test的对象实例

这个结果完全符合我们的预期,第二个ObjectHolder的指针是在Java堆中JHSDB_TestCase$Test对象的instanceObj字段上。

但是我们采用相同方法查找第三个ObjectHolder实例时,JHSDB返回了一个null,表示未查找到任何结果:

看来revptrs命令并不支持查找栈上的指针引用,不过没有关系,得益于我们测试代码足够简洁,
人工也可以来完成这件事情。在Java Thread窗口选中main线程后点击Stack Memory按钮查看该线程的栈内存。

ps: 中间截图截一半时,电脑突然花屏 重启,幸好没啥事。

(Ashiamd的github个人学习笔记)[https://ashiamd.github.io/docsify-notes/#/README] ~小尾巴~
原文地址:https://www.cnblogs.com/Ashiamd/p/14054498.html