经过HSDB来了解String值的真身在哪里

  最近经过@RednaxelaFX的一篇文章得知了HSDB,并好好研究了一下用法,对学习jvm的人来讲绝对是一个利器,能够摆脱GDB,直接图形化看内存结构布局,具体的用法我就很少说了,这篇文章介绍得很详细了,此次写文章主要是想经过这一利器来分析下String的值在java里的内存状况,不一样场景下的String的值究竟是在内存里的哪块区域,这里强调的是值,并非对象,由于对象咱们都知道是存在heap里的,咱们看java.lang.String的源码会看到有一个value数组,这里才是真正的值,本文顺带也是hsdb用法的一个介绍,如此利器但愿给你们带来不同的乐趣。 java

  仍是先看demo windows

public class StringTest {      private String val1="a";      private static String val2=     StringTest.class.getName()+"b"; public static void main(String args[]){ StringTest st=new StringTest(); String a="a"; String d="a"; String b=a+"b"; String c="a"+"b"; String e="ab"; System.out.println(a+b+c+d+e); } }

  本文想从上面的例子得出哪些结论呢? centos

1. 实例变量val1和局部变量a,d是否指向同一个内存地址 2. 局部变量b,c,e是否指向同一个内存地址 3. 局部变量b的值是在哪里分配的,stack?heap?perm? 4. 字符常量”a”,”ab”分配在哪里? 5. 静态变量val2的值又是分配在哪里?

  先看看咱们经过eclipse调试能肯定的结果,断点打在最后一行 数组

  获得初步结论: eclipse

1. 实例变量val1和局部变量a,d里的value值都是指向同一个id25的值 2. 局部变量ce指向了同一个id28的值 3. 局部变量bce不是指向同一个地方,有一个面值相同的值在另一个内存区域

  接下来咱们经过hsdb来验证下上面的结论,以及解答剩下的疑惑 jvm

  操做步骤以下: oop

1. 设置断点在`System.out.println(a+b+c+d+e);`这一行,vm参数设置`-XX:+UseSerialGC -Xmx10m` 2. 经过jps命令获取对应的pid 3. 而后经过以下命令打开hsdb`java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB,(若是是windows请替换$JAVA_HOME为%JAVA_HOME%)` 4. 点击File->Attach to...输入pid 此时你会看到以下界面:

这是一个线程列表,咱们选择main这个线程,也就是咱们的主线程 5. 点击面板里第二个图标

获得以下图

这个图实际上是咱们main方法的栈帧,由于咱们目前只有一层调用,还在main方法里,看到咱们圈起来的那部份内容,看到好多String对象既有在PermGen里的,也有在NewGen里的,那么每个具体是什么值呢,是对应咱们代码里的那些局部变量吗,若是是的话,哪一个对应哪一个呢 6. 点击大窗口里的windows->Console

获得命令行控制台窗口,在窗口内敲回车,会看到以下界面

7. 在命令行窗口里输入universe,先获得每一个分区的内存范围,因为格式的问题,我就直接copy出来了

8. 从上面的main方法栈帧里咱们分别取查看那些String对象在内存里的位置(第二列地址就是对象的地址,第一列是栈帧里每部分的内存地址),先按照以下菜单调出查看内存结构的窗口

弹出窗口以后相似下面的操做,在2处输入1的地址,1处圈起来的是紫色标注的String对象的内存地址,细心的读者可能发现了,1处的地址在上面的每一个分区内存块的PSPermGen里,这说明这个值为abString对象是在perm区的,这个对象的char数组的地址,也就是下面的标注3处的

其实咱们看到的那几个String对象的顺序是对应咱们声明的局部变量的逆序,也就是ecbda,最后那个StringTest就是局部变量st,后面的ObjArray实际上是咱们main方法传进的字符数组,这个其实咱们经过javap -verbose StringTest能够查到

哪一个solt对应哪一个局部变量都有写的,要想看到这个必须在编译的时候要加上-g参数才行 下面再查找下StringTest这个对象的内存值

经过分别对比每一个String对象和StringTest对象的内存地址和每一个区的内存地址范围,咱们能得出的结论是 * 局部变量a,d,c,e是在perm区的 * 局部变量bst是在eden区的,可是stval1的值又是在perm区的 * 同时能验证上面一开始得出的三个结论 * 咱们也没看到有对象在栈上分配,只看到栈上持有对象的引用,所以当栈回收的时候只是将引用给回收了,具体的对象值仍是在内存里 9. 接下来是要找到静态变量val2,在命令行中输入`mem 0x00000000f5043360 2`,由于val2做为静态变量是和class关联的,所以要找到对象的class,若是了解java对象的内存结构的话咱们知道每一个oop都有一个head,这个head由两部分组成,一个是mark,另外一个是_klass,所以经过memoop的内存地址取连续的两个字宽,第二个字宽就是咱们要的klass

这里估计是一个bug,不能全取,咱们只能取后面的8位才行,也就是0xf5ccef60,而后按照第8步的方式输入上面的内存地址,在最后咱们看到val2

对比内存分代咱们获得这个地址是在eden区的,也就是在heap里分配的,另外若是你加一个赋值常量的静态变量,你会发现竟然是在perm区的,这个就你们本身去验证吧

注:以上结论都是在centos系统jdk6上进行验证的,jdk7可能有所不同. 布局

http://lovestblog.cn/blog/2014/06/28/hsdb-string/
相关文章
相关标签/搜索