引言
很多人认为jvm字符串常量不会被回收的,其实这个说法的有误区的,我们通过一些jvm参数可以看到StringTable的垃圾回收。
案例说明
参数说明
参数 | 说明 |
-Xmx10m | 堆空间大小 |
-XX+PrintStringTableStatistics | 打印串池统计信息 |
-XX:+PrintGCDetails | 打印GC日志详情 |
-verbose:gc | 打印GC日志 |
先来一段空程序,我们使用参数运行:
-Xmx10m -XX+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
public static void main(String[] args) { int i=0; try { }catch (Throwable e){ e.printStackTrace(); }finally { System.out.println(i); } }
结果如下:
0 Heap PSYoungGen total 2560K, used 1388K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000) eden space 2048K, 67% used [0x00000007bfd00000,0x00000007bfe5b100,0x00000007bff00000) from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000) to space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000) ParOldGen total 7168K, used 0K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000) object space 7168K, 0% used [0x00000007bf600000,0x00000007bf600000,0x00000007bfd00000) Metaspace used 2927K, capacity 4496K, committed 4864K, reserved 1056768K class space used 319K, capacity 388K, committed 512K, reserved 1048576K SymbolTable statistics: Number of buckets : 20011 = 160088 bytes, avg 8.000 Number of entries : 11756 = 282144 bytes, avg 24.000 Number of literals : 11756 = 455312 bytes, avg 38.730 Total footprint : = 897544 bytes Average bucket size : 0.587 Variance of bucket size : 0.587 Std. dev. of bucket size: 0.766 Maximum bucket size : 6 StringTable statistics: Number of buckets : 60013 = 480104 bytes, avg 8.000 Number of entries : 831 = 19944 bytes, avg 24.000 Number of literals : 831 = 56304 bytes, avg 67.755 Total footprint : = 556352 bytes Average bucket size : 0.014 Variance of bucket size : 0.014 Std. dev. of bucket size: 0.118 Maximum bucket size : 2
我们看到程序输出了堆信息,以及串池统计信息,需要了解的是我们的串池实现其实是类似Map的存储结构,里面的存储结构是数组的方式,每一个bucket就是一个数组结构。
Number of buckets : 60013 = 480104 bytes, avg 8.000 Number of entries : 831 = 19944 bytes, avg 24.000 Number of literals : 831 = 56304 bytes, avg 67.755
代表了里面存储的字符串个数。
我们假设如下,如果不存在gc,我们写入10万个字符,那么Number of entries 会增加到10万。我们调整程序如下:
int i=0; try { for(int j=0;j<100000;j++){ String.valueOf(j).intern(); i++; } }catch (Throwable e){ e.printStackTrace(); }finally { System.out.println(i); }
运行查看结果:
[GC (Allocation Failure) [PSYoungGen: 2048K->496K(2560K)] 2048K->504K(9728K), 0.0015310 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2544K->512K(2560K)] 2552K->528K(9728K), 0.0010023 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2560K->480K(2560K)] 2576K->520K(9728K), 0.0013527 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 100000 Heap PSYoungGen total 2560K, used 1295K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000) eden space 2048K, 39% used [0x00000007bfd00000,0x00000007bfdcbc60,0x00000007bff00000) from space 512K, 93% used [0x00000007bff00000,0x00000007bff78020,0x00000007bff80000) to space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000) ParOldGen total 7168K, used 40K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000) object space 7168K, 0% used [0x00000007bf600000,0x00000007bf60a000,0x00000007bfd00000) Metaspace used 3095K, capacity 4496K, committed 4864K, reserved 1056768K class space used 339K, capacity 388K, committed 512K, reserved 1048576K SymbolTable statistics: Number of buckets : 20011 = 160088 bytes, avg 8.000 Number of entries : 12129 = 291096 bytes, avg 24.000 Number of literals : 12129 = 467440 bytes, avg 38.539 Total footprint : = 918624 bytes Average bucket size : 0.606 Variance of bucket size : 0.606 Std. dev. of bucket size: 0.778 Maximum bucket size : 6 StringTable statistics: Number of buckets : 60013 = 480104 bytes, avg 8.000 Number of entries : 15285 = 366840 bytes, avg 24.000 Number of literals : 15285 = 866120 bytes, avg 56.665 Total footprint : = 1713064 bytes Average bucket size : 0.255 Variance of bucket size : 0.267 Std. dev. of bucket size: 0.516 Maximum bucket size : 4
我们发现日志显示发生了三次Yong gc,Number of entries 是15285,没有达到10万,说明了我们的串池是会发生GC的。