jdk1.8之后,JVM内存结构如下

所谓的StringTable,即字符串常量池(以下简称串池),存放在堆内存中。
我们先介绍一下intern方法
- String s = "ab";
- //将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回
- String s2 = s.intern();
调优方法1.因为StringTable是由HashTable实现的,所以可以适当增加HashTable桶的个数,来减少字符串放入串池所需要的时间。
示例代码:遍历文本文件,读取每一行的内容放入串池
- public class Demo1_24 {
-
- public static void main(String[] args) throws IOException {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
- String line = null;
- long start = System.nanoTime();
- while (true) {
- line = reader.readLine();
- if (line == null) {
- break;
- }
- line.intern();
- }
- System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
- }
-
-
- }
- }
linux.words是存放文本的文件(实际文本有四百多万行,只粘贴部分做示例)
- 1080
- 10-point
- 10th
- 11-point
- 12-point
- 16-point
- 18-point
- 1st
- 2
- 20-point
- 2,4,5-t
- 2,4-d
- 2D
- 2nd
- 30-30
- 3-D
- 3-d
- 3D
- 3M
- 3rd
- 48-point
- 4-D
- 4GL
- 4H
- 4th
- 5-point
- 5-T
- 5th
- 6-point
- 6th
- 7-point
- 7th
- 8-point
- 8th
- 9-point
- 9th
- -a
- A
- A.
- a
- a'
- a-
- a.
- A-1
- A1
- a1
- A4
- A5
- AA
- aa
我们运行程序,发现耗时不到1s

然后我们设置JVM运行参数,修改桶的数量为1009,再次运行
-XX:+PrintStringTableStatistics -XX:StringTableSize=1009

运行结果耗时8秒 如下图

StringTable默认的桶的数量是60013,当桶的数量设置变小,哈希碰撞的概率增加,链表长度
变长,数据插入就会变慢,可以适当增加HashTable桶的个数,来减少字符串放入串池所需要的时间。
调优方法2:如果应用里有大量的字符串,而且字符串可能存在重复的问题,可以通过intern方法让字符串入池,减少字符串个数(触发垃圾回收 没入池的字符串被回收掉),节约堆内存的使用
示例代码:同样是遍历上述的文本文件,将每一行的内容放入ArrayList中,循环操作10次
- public class Demo1_25 {
- public static void main(String[] args) throws IOException {
- List<String> address = new ArrayList<>();
- System.in.read();
- for (int i = 0; i < 10; i++) {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
- String line = null;
- long start = System.nanoTime();
- while (true) {
- line = reader.readLine();
- if(line == null) {
- break;
- }
- address.add(line);
- }
- System.out.println("cost:" +(System.nanoTime()-start)/1000000);
- }
- }
- System.in.read();
- }
- }
运行以上代码,我们控制台输入
jvisualvm
查看字符串所占用的内存使用情况
然后我们敲下Enter执行我们的主程序,遍历文本文件,发现内存占用飙高很多

然后我们修改代码(只改了一行代码,address.add(line.intern())
- public class Demo1_25 {
- public static void main(String[] args) throws IOException {
- List<String> address = new ArrayList<>();
- System.in.read();
- for (int i = 0; i < 10; i++) {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
- String line = null;
- long start = System.nanoTime();
- while (true) {
- line = reader.readLine();
- if(line == null) {
- break;
- }
- //仅修改这一处代码
- address.add(line.intern());
- }
- System.out.println("cost:" +(System.nanoTime()-start)/1000000);
- }
- }
- System.in.read();
- }
- }
再运行查看内存占用情况,下降了很多
我们循环遍历10次,会在堆中产生很多重复的字符串,而ArrayList中存放的对象都来自串池,堆中的字符串 如果没被引用,会被垃圾回收掉,从而节约了堆内存的使用