最近經過@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值都是指向同一個id爲25的值 2. 局部變量c和e指向了同一個id爲28的值 3. 局部變量b和c,e不是指向同一個地方,有一個面值相同的值在另一個內存區域
接下來咱們經過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裏,這說明這個值爲ab的String對象是在perm區的,這個對象的char數組的地址,也就是下面的標註3處的
其實咱們看到的那幾個String對象的順序是對應咱們聲明的局部變量的逆序,也就是e,c,b,d,a,最後那個StringTest就是局部變量st,後面的ObjArray實際上是咱們main方法傳進的字符數組,這個其實咱們經過javap -verbose StringTest能夠查到
哪一個solt對應哪一個局部變量都有寫的,要想看到這個必須在編譯的時候要加上-g參數才行 下面再查找下StringTest這個對象的內存值
經過分別對比每一個String對象和StringTest對象的內存地址和每一個區的內存地址範圍,咱們能得出的結論是 * 局部變量a,d,c,e是在perm區的 * 局部變量b和st是在eden區的,可是st的val1的值又是在perm區的 * 同時能驗證上面一開始得出的三個結論 * 咱們也沒看到有對象在棧上分配,只看到棧上持有對象的引用,所以當棧回收的時候只是將引用給回收了,具體的對象值仍是在內存裏 9. 接下來是要找到靜態變量val2,在命令行中輸入`mem 0x00000000f5043360 2`,由於val2做爲靜態變量是和class關聯的,所以要找到對象的class,若是瞭解java對象的內存結構的話咱們知道每一個oop都有一個head,這個head由兩部分組成,一個是mark,另外一個是_klass,所以經過mem對oop的內存地址取連續的兩個字寬,第二個字寬就是咱們要的klass
這裏估計是一個bug,不能全取,咱們只能取後面的8位才行,也就是0xf5ccef60,而後按照第8步的方式輸入上面的內存地址,在最後咱們看到val2
對比內存分代咱們獲得這個地址是在eden區的,也就是在heap裏分配的,另外若是你加一個賦值常量的靜態變量,你會發現竟然是在perm區的,這個就你們本身去驗證吧
注:以上結論都是在centos系統jdk6上進行驗證的,jdk7可能有所不同. 佈局
http://lovestblog.cn/blog/2014/06/28/hsdb-string/