天天看點

JVM筆記-黑馬-2

文章目錄

  • ​​視訊資源位址​​
  • ​​筆記資源位址​​
  • ​​我的筆記​​
  • ​​27.stringtable·面試題 + 28.常量池與串池的關系​​
  • ​​29.字元串變量拼接​​
  • ​​30.編譯器優化​​
  • ​​31.字元串延遲加載​​
  • ​​32-33.stringtable_intern_1.8與1.6​​
  • ​​34. string_table 面試題​​
  • ​​35-36. string_table 位置​​
  • ​​37. string_table 垃圾回收​​
  • ​​38-40. string_table 性能調優​​

視訊資源位址

B站 https://www.bilibili.com/video/av70549061

筆記資源位址

​​https://nyimac.gitee.io/​​

我的筆記

27.stringtable·面試題 + 28.常量池與串池的關系

首先寫一段聲明字元串的代碼,通過反編譯檢視他的常量池的樣子。

JVM筆記-黑馬-2

關鍵指令:javap -v class位址

JVM筆記-黑馬-2

看位元組碼的内容,這一行:去常量池2位置中加載資訊。 然後存入局部變量1中。如下圖局部變量表。

JVM筆記-黑馬-2

以此類推,下圖中三個變量都是這樣加載進來,并指派在局部變量表的。

JVM筆記-黑馬-2

常量池是存在位元組碼檔案中的,上面這個例子已經證明了,但是這個靜态的,當它運作起來之後,就會被加載到運作時常量池。如下圖這些都會被加載到運作時常量池:

JVM筆記-黑馬-2

注意:

JVM筆記-黑馬-2

當位元組碼執行到ldc的時候,才會将符号跟java對象聯系起來。

JVM筆記-黑馬-2

此時,會去檢視Stringtable,。也就是所謂的串池,有沒有包含a,如果不包含就加入進去。

這個行為是一個懶惰的行為,代碼中用到這個對象,才會去串池中查找或者添加。

原文筆記:

JVM筆記-黑馬-2

29.字元串變量拼接

反編譯之後如下圖所示。相當于執行了注釋中的操作。

JVM筆記-黑馬-2

問題:

JVM筆記-黑馬-2

答案是fasle。因為s3在串池裡,s4在堆裡。 tostring方法的本質是調用了new string(“ab”) 方法。他倆的位置不一樣,是兩個對象。

30.編譯器優化

JVM筆記-黑馬-2

建立一個string s5 = “a” + “b”; 然後反編譯。

他是直接去常量池中加載的#4這個常量。并且存入了5号局部變量表。

是以s3和s5的變量都是存儲的串池中的同一個字元串常量,是以他倆是相等的。

“a” + “b” 是 javac在編譯期的優化。 這倆都是常量,相加不會變了,結果在編譯期間已經确定為“ab”。

跟s4不一樣,s4的生成是兩個變量相加,需要動态拼接。就到了堆中了。

31.字元串延遲加載

JVM筆記-黑馬-2
JVM筆記-黑馬-2

上面這個例子。隻會在串池中建立10個對象,再下面繼續列印,就是用的串池中的了。

32-33.stringtable_intern_1.8與1.6

JVM筆記-黑馬-2

調用字元串對象的intern方法,會将該字元串對象嘗試放入到串池中.

  • 如果串池中沒有該字元串對象,則放入成功
  • 如果有該字元串對象,則放入失敗

    無論放入是否成功,都會傳回串池中的字元串對象

    注意:此時如果調用intern方法成功,堆記憶體與串池中的字元串對象是同一個對象;如果失敗,則不是同一個對象。

public class Main {
  public static void main(String[] args) {
    //"a" "b" 被放入串池中,str則存在于堆記憶體之中
    String str = new String("a") + new String("b");
    //調用str的intern方法,這時串池中沒有"ab",則會将該字元串對象放入到串池中,此時堆記憶體與串池中的"ab"是同一個對象
    String st2 = str.intern();
    //給str3指派,因為此時串池中已有"ab",則直接将串池中的内容傳回
    String str3 = "ab";
    //因為堆記憶體與串池中的"ab"是同一個對象,是以以下兩條語句列印的都為true
    System.out.println(str == st2);
    System.out.println(str == str3);
  }
}      
public class Main {
  public static void main(String[] args) {
        //此處建立字元串對象"ab",因為串池中還沒有"ab",是以将其放入串池中
    String str3 = "ab";
        //"a" "b" 被放入串池中,str則存在于堆記憶體之中
    String str = new String("a") + new String("b");
        //此時因為在建立str3時,"ab"已存在與串池中,是以放入失敗,但是會傳回串池中的"ab"
    String str2 = str.intern();
        //false
    System.out.println(str == str2);
        //false
    System.out.println(str == str3);
        //true
    System.out.println(str2 == str3);
  }
}      

intern方法 1.6 :

調用字元串對象的intern方法,會将該字元串對象嘗試放入到串池中:

  • 如果串池中沒有該字元串對象,會将該字元串對象複制一份,再放入到串池中
  • 如果有該字元串對象,則放入失敗

無論放入是否成功,都會傳回串池中的字元串對象。

注意:此時無論調用intern方法成功與否,串池中的字元串對象和堆記憶體中的字元串對象都不是同一個對象

34. string_table 面試題

JVM筆記-黑馬-2

19與20行調換位置之前: 最後的答案是true,因為jdk1.8中,x2會從堆中也把同一對象放到串池中,是以x1=“cd”,直接從串池拿的對象。

19與20行調換位置之後:false,x1的“cd”來自于串池,是建立的,x2來自于堆,把x2放入串池的操作,實際晚了一步。

jdk1.6,調換位置之前:false,因為1.6會從堆中複制一個對象去串池,實際上兩個對象不同。

jdk1.6,調換位置之後:false,一個是堆中的,一個是串池的,2個對象不同。intern沒起到作用。

35-36. string_table 位置

JVM筆記-黑馬-2

1.6 :string table 是常量池的一部分,跟常量池一起存在永久代中,永久代的記憶體回收效率很低,隻有在full gc的時候才會觸發垃圾回收機制,full gc隻在老年代空間不足時候才會觸發,觸發的時機不對,但是stringtable用的是十分頻繁的,這種垃圾回收頻顯然不行。1.7之後,stringtable就轉移到了堆中,年輕代的垃圾回收機制很頻繁,是以性能好。驗證程式如下:

JVM筆記-黑馬-2

1.6版本報錯:

JVM筆記-黑馬-2

1.8版本報錯:并沒有出現heap關鍵詞

JVM筆記-黑馬-2
JVM筆記-黑馬-2

jvm原文:如果百分之98的時間用來垃圾回收,就說明癌症晚期。就會報上面的錯,也說明堆空間不足了。如果把這個功能 -XX:-xxxx,就可以出現堆記憶體溢出了。

JVM筆記-黑馬-2

37. string_table 垃圾回收

stringtable在堆中,雖然是字元串常量,那麼也得被垃圾回收。

将jvm做如下設定:

JVM筆記-黑馬-2

Xmx:規定堆記憶體的大小

-XX:+pringtStringTableStatistics 答應串池的統計資訊

後面兩個參數:列印垃圾回收的相關資訊

運作一段代碼,代碼沒有做任何事情。

JVM筆記-黑馬-2

列印内容如下:

JVM筆記-黑馬-2

上述是堆的資訊:新生代,老年代,元空間分别的大小喝使用情況。

JVM筆記-黑馬-2

符号統計:類的位元組碼裡那些類名、方法名、變量名,也是需要讀入到記憶體中,以查表的方式去讀出來。也是屬于常量池的一部分。

JVM筆記-黑馬-2

stringtable的統計資訊:底層的實作是hashtable,是數組+連結清單的結構。

主要看前三個number,number of literal 是說明的字元串常量的統計。因為jvm中本身是有很多字元串常量的。

這時候,main函數中增加字元串常量往串池中intern的操作。

JVM筆記-黑馬-2

運作結果如下:增加了100個對象

JVM筆記-黑馬-2

如果增加的常量變成1w個,那麼10m的堆空間肯定不夠,就會觸發gc了。

JVM筆記-黑馬-2

實際上隻存進去7000多(含有本身的1754)。過程中肯定因為記憶體配置設定失敗觸發了gc。無用的字元串常量被清掉了。

JVM筆記-黑馬-2

38-40. string_table 性能調優

stringtable底層是一個hash表,性能跟他的大小相關。

tips:垃圾回收隻有在記憶體緊張時才會觸發。

JVM筆記-黑馬-2

檔案中有50w行關鍵字資料。流讀入之後,放入串池。

首先,進行jvm的參數設定,

JVM筆記-黑馬-2

進行入池:0.4秒,速度是非常快的。

JVM筆記-黑馬-2
JVM筆記-黑馬-2

然後把stringtablesize 這個配置去掉,性能明顯降低了。這個參數是調節預設桶大小的,大了,性能會高。

JVM筆記-黑馬-2

建議:如果你的系統中字元串常量很多,建議調大stringtablesize,必須大于1009,預設是60000.減少has沖突,提升效率。

調優:對于位址等資訊,拆分省市縣區和具體位址,然後用串池的方法載入到内容,比一次性全部載入要節省空間。

demo實驗,證明調優:

JVM筆記-黑馬-2

不加line.intern():

JVM筆記-黑馬-2