聊聊jvm的StringTable及SymbolTable

go4it發表於2019-04-08

本文主要研究一下jvm的StringTable及SymbolTable

StringTable及SymbolTable

JDK的變動

在java7的時候將字串常量池移到java heap,字串常量池被限制在整個應用的堆記憶體中,在執行時呼叫String.intern()增加字串常量不會使永久代OOM了。使用-XX:StringTableSize可以設定StringTableSize,預設是65536

java8的時候去除PermGen,將其中的方法區移到non-heap中的Metaspace,因而SymbolTable也跟隨Metaspace移到了non-heap中

SymbolTable

symbolic references in Runtime Constant Pool

  • 一個完整的類載入過程必須經歷載入(Loading)、連線(Linking)、初始化(Initialization)這三個步驟
  • 其中類載入階段就是由類載入器負責根據一個類的全限定名來讀取此類的二進位制位元組流到JVM內部,然後將其轉換為一個與目標型別對應的java.lang.Class物件例項;連線階段要做的是將載入到JVM中的二進位制位元組流的類資料資訊合併到JVM的執行時狀態中,經由驗證(Verification)、準備(Preparation)、解析(Resolution)三個階段;初始化階段將一個類中所有被static關鍵字標識的程式碼統一執行一遍,如果執行的是靜態變數,那麼就會使用使用者指定的值覆蓋之前在準備階段設定的初始值;如果執行的是static程式碼塊,那麼在初始化階段,JVM就會執行static程式碼塊中定義的所有操作
  • 在連線(Linking)步驟裡頭的解析(Resolution)階段,需要將常量池中所有的符號引用(classes、interfaces、fields、methods referenced in the constant pool)轉為直接引用(得到類或者欄位、方法在記憶體中的指標或者偏移量,以便直接呼叫該方法)

SymbolTable這個詞在傳統程式語言的實現裡頭比較常用(This data structure serves many of the purposes of the symbol table of a conventional programming language implementation),而在jvm裡頭對應的是Runtime Constant Pool中的symbolic references(Runtime Constant Pool除了symbolic references還包含了static constants),它是在類載入的時候(Resolution in Linking)根據class後設資料中的constant pool table建立的,因而稱為Runtime Constant Pool;這部分屬於metaspcae,在native memory中

檢視StringTable

/ # jcmd 1 VM.stringtable
1:
StringTable statistics:
Number of buckets       :     65536 =    524288 bytes, each 8
Number of entries       :     23407 =    374512 bytes, each 16
Number of literals      :     23407 =   2153344 bytes, avg  91.996
Total footprsize_t         :           =   3052144 bytes
Average bucket size     :     0.357
Variance of bucket size :     0.360
Std. dev. of bucket size:     0.600
Maximum bucket size     :         5
複製程式碼
  • 使用jcmd pid VM.stringtable可以在執行時檢視

檢視SymbolTable

/ # jcmd 1 VM.symboltable
1:
SymbolTable statistics:
Number of buckets       :     32768 =    262144 bytes, each 8
Number of entries       :    128885 =   2062160 bytes, each 16
Number of literals      :    128885 =   7160912 bytes, avg  55.560
Total footprsize_t         :           =   9485216 bytes
Average bucket size     :     3.933
Variance of bucket size :     3.982
Std. dev. of bucket size:     1.996
Maximum bucket size     :        14
複製程式碼
  • 使用jcmd pid VM.symboltable可以在執行時檢視

同時檢視StringTable及SymbolTable

-XX:+PrintStringTableStatistics

SymbolTable statistics:
Number of buckets       :     32768 =    262144 bytes, each 8
Number of entries       :    129215 =   2067440 bytes, each 16
Number of literals      :    129215 =   7173248 bytes, avg  55.514
Total footprsize_t         :           =   9502832 bytes
Average bucket size     :     3.943
Variance of bucket size :     3.990
Std. dev. of bucket size:     1.998
Maximum bucket size     :        14
StringTable statistics:
Number of buckets       :     65536 =    524288 bytes, each 8
Number of entries       :     23470 =    375520 bytes, each 16
Number of literals      :     23470 =   2157736 bytes, avg  91.936
Total footprsize_t         :           =   3057544 bytes
Average bucket size     :     0.358
Variance of bucket size :     0.361
Std. dev. of bucket size:     0.601
Maximum bucket size     :         5
複製程式碼
  • 啟動時新增-XX:+PrintStringTableStatistics引數,在jvm程式退出時會輸出SymbolTable statistics及StringTable statistics

jcmd pid VM.native_memory

/ # jcmd 1 VM.native_memory scale=MB
1:

Native Memory Tracking:

Total: reserved=1857MB, committed=112MB
-                 Java Heap (reserved=502MB, committed=32MB)
                            (mmap: reserved=502MB, committed=32MB)

-                     Class (reserved=1065MB, committed=47MB)
                            (classes #8386)
                            (  instance classes #7843, array classes #543)
                            (malloc=1MB #21250)
                            (mmap: reserved=1064MB, committed=45MB)
                            (  Metadata:   )
                            (    reserved=40MB, committed=40MB)
                            (    used=39MB)
                            (    free=1MB)
                            (    waste=0MB =0.00%)
                            (  Class space:)
                            (    reserved=1024MB, committed=6MB)
                            (    used=5MB)
                            (    free=0MB)
                            (    waste=0MB =0.00%)

-                    Thread (reserved=29MB, committed=3MB)
                            (thread #29)
                            (stack: reserved=29MB, committed=2MB)

-                      Code (reserved=243MB, committed=15MB)
                            (malloc=1MB #4744)
                            (mmap: reserved=242MB, committed=14MB)

-                        GC (reserved=2MB, committed=0MB)
                            (mmap: reserved=2MB, committed=0MB)

-                  Internal (reserved=1MB, committed=1MB)
                            (malloc=1MB #2172)

-                    Symbol (reserved=10MB, committed=10MB)
                            (malloc=7MB #223735)
                            (arena=3MB #1)

-    Native Memory Tracking (reserved=4MB, committed=4MB)
                            (tracking overhead=4MB)
複製程式碼
  • 使用jcmd pid VM.native_memory輸出的Symbol部分包含了StringTable(interned String)及SymbolTable

小結

  • 在java7的時候將字串常量池則移到java heap,字串常量池被限制在整個應用的堆記憶體中,在執行時呼叫String.intern()增加字串常量不會使永久代OOM了。使用-XX:StringTableSize可以設定StringTableSize,預設是65536;java8的時候去除PermGen,將其中的方法區移到non-heap中的Metaspace,因而SymbolTable也跟隨Metaspace移到了non-heap中
  • StringTable位於heap中(java7+),而SymbolTable則在native memory中;使用jcmd pid VM.stringtable可以在執行時檢視StringTable;使用jcmd pid VM.symboltable可以在執行時檢視SymbolTable
  • 在啟動時新增-XX:+PrintStringTableStatistics引數,在jvm程式退出時會輸出SymbolTable statistics及StringTable statistics;使用jcmd pid VM.native_memory輸出的Symbol部分包含了heap中StringTable(interned String)及non heap中的SymbolTable

doc

相關文章