聊聊jvm的StringTable及SymbolTable

40次阅读

共计 4513 个字符,预计需要花费 12 分钟才能阅读完成。


本文主要研究一下 jvm 的 StringTable 及 SymbolTable
StringTable 及 SymbolTable
JDK 的变动
在 java7 的时候将字符串常量池移到 java heap,字符串常量池被限制在整个应用的堆内存中,在运行时调用 String.intern()增加字符串常量不会使永久代 OOM 了。使用 -XX:StringTableSize 可以设置 StringTableSize,默认是 65536java8 的时候去除 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

Java 的类加载机制
Chapter 5. Loading, Linking, and Initializing
5.1. The Run-Time Constant Pool
Understanding String Table Size in HotSpot
聊聊 jvm 的 PermGen 与 Metaspace
Will Java’s interned strings be GCed?
Garbage collection behaviour for String.intern()
10 Things Every Java Programmer Should Know about String
Difference between String literal and New String object in Java
Understand JVM Loading, JVM Linking, and JVM Initialization

正文完
 0