文章目录

1.StringTable特性

  • 常量池中的字符串仅是符号,第一次用到时才变成对象。
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是​​StringBuilder(1.8)​
  • 字符串常量拼接的原理是编译期优化
  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池
  • 1.8将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,两种情况均会把串池中的对象返回。
  • 1.6将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,两种情况均会把串池中的对象返回。即调用intern的对象,和将来放入串池的对象,是两个对象。
public class Demo1_23 {

// ["ab", "a", "b"]
public static void main(String[] args) {

String x = "ab";
String s = new String("a") + new String("b");

// 堆 new String("a") new String("b") new String("ab")
String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

System.out.println( s2 == x);//true
System.out.println( s == x );//false
}

}
  1. 执行到第10行String s2 = s.intern(); 时,将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回。由于String x = “ab”;StringTable中有"ab"了,所以s不会放在常量池。但是会吧常量池的"ab"返回回来给s2。
    所以 s2 == x 即 常量池的"ab"
  2. s != x,因为s没有放进去
public class Demo1_223 {

// ["a", "b","ab" ]
public static void main(String[] args) {

String s = new String("a") + new String("b");

// 堆 new String("a") new String("b") new String("ab")
String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

System.out.println( s2 == "ab"); //true
System.out.println( s == "ab" ); //true
}
}
  1. 执行到第10行String s2 = s.intern(); 时,将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回。此时StringTable还没有"ab",所以s会放在常量池。同时常量池的"ab"返回回来给s2。
    所以 s2 == x 即 常量池的"ab"
  2. s == x,因为s放进去了

JDK1.6运行情况:

public class Demo111 {

// ["a", "b","ab" ]
public static void main(String[] args) {

String s = new String("a") + new String("b");

// 堆 new String("a") new String("b") new String("ab")
String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

System.out.println( s2 == "ab"); //true
System.out.println( s == "ab" ); //false
}

}

JVM专题(七)-StringTable_字符串

2.StringTable面试题

public class Demo1_21 {

public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "a"+"b"; // 优化为"ab"
String s4 = s1 + s2; // new StringBuilder 在堆中
String s5 = "ab"; // 去串池查找添加
String s6 = s4.intern();

System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true
System.out.println(s3 == s6); // true

String x2 = new String("c") + new String("d"); // new String("cd")
String x1 = "cd"; //"cd"
x2.intern(); // 串池已经有cd,x2放不进去
System.out.println(x1 == x2); // false
}
}

如果调换位置

String x2 = new String("c") + new String("d"); // new String("cd")
x2.intern(); // 串池已没有cd,x2放进去
String x1 = "cd"; //"cd"

System.out.println(x1 == x2); // true

3.StringTable位置

JVM专题(七)-StringTable_java_02

  • 在1.6之前。StringTable在方法区永久代中,但是放在这之中,虚拟机只会在full GC时才会对StringTable进行垃圾回收。但是其实StringTable的操作是非常频繁的,如果没有即使进行垃圾回收,容易造成永久代空间不足。
  • 在1.8后,StringTable放在了堆中,使其垃圾回收的效率更高。

4.案例

JDK1.6下

虚拟机参数

-XX:MaxPermSize=10m  //永久代空间设置
package com.bruce.test;


import java.util.ArrayList;
import java.util.List;

/**
* 演示 StringTable 位置
* 在jdk8下设置 -Xmx10m -XX:-UseGCOverheadLimit
* 在jdk6下设置 -XX:MaxPermSize=10m
*/
public class Demo1_6 {

public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
int i = 0;
try {
for (int j = 0; j < 260000; j++) {
list.add(String.valueOf(j).intern());
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
}
}

JVM专题(七)-StringTable_垃圾回收_03
JDK1.8下

虚拟机参数

// 堆空间大小设置
-Xmx10m
public class Demo1_221 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
int i = 0;
try {
for (int j = 0; j < 260000; j++) {
list.add(String.valueOf(j).intern());
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
}
}

JVM专题(七)-StringTable_字符串_04
JVM专题(七)-StringTable_java_05
添加JVM参数:

// 堆空间大小设置、关闭UseGCOverheadLimit
-Xmx10m -XX:-UseGCOverheadLimit

JVM专题(七)-StringTable_开发语言_06

可以看到1.6是永久代空间溢出,1.8是堆空间溢出。

5.StringTable垃圾回收

StringTable中的字符串常量不再引用后,也会被垃圾回收。

package com.method.area;

/**
* 演示 StringTable 垃圾回收
* -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
*/
public class Demo1_231 {
public static void main(String[] args) {
int i = 0;
try {
for (int j = 0; j < 10; j++) {
//String.valueOf(j).intern();
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
}
}

JVM专题(七)-StringTable_开发语言_07

public class Demo1_23 {
public static void main(String[] args) {
int i = 0;
try {
for (int j = 0; j < 10; j++) {
String.valueOf(j).intern();
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
}
}

JVM专题(七)-StringTable_开发语言_08

for (int j = 0; j < 100000; j++) {
String.valueOf(j).intern();
i++;
}

JVM专题(七)-StringTable_后端_09
JVM专题(七)-StringTable_后端_10
清理了一些未引用的字符串常量。