【JVM之内存与垃圾回收篇】StringTable (2)

【JVM之内存与垃圾回收篇】StringTable

【JVM之内存与垃圾回收篇】StringTable

【JVM之内存与垃圾回收篇】StringTable

为什么StringTable从永久代调整到堆中

官网说明:https://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

在 JDK 7 中,interned 字符串不再在 Java 堆的永久生成中分配,而是在 Java 堆的主要部分(称为年轻代和年老代)中分配,与应用程序创建的其他对象一起分配。此更改将导致驻留在主 Java 堆中的数据更多,驻留在永久生成中的数据更少,因此可能需要调整堆大小。由于这一变化,大多数应用程序在堆使用方面只会看到相对较小的差异,但加载许多类或大量使用字符串的较大应用程序会出现这种差异。intern() 方法会看到更显著的差异。

原因:

永久代的默认比较小

永久代垃圾回收频率低

String 的基本操作

Java 语言规范里要求完全相同的字符串字面量,应该包含同样的 Unicode 字符序列(包含同一份码点序列的常量),并且必须是指向同一个 String 类实例。

【JVM之内存与垃圾回收篇】StringTable

字符串拼接操作

常量与常量的拼接结果在常量池,原理是编译期优化

常量池中不会存在相同内容的变量

只要其中有一个是变量,结果就在堆中。变量拼接的原理是 StringBuilder

如果拼接的结果调用 intern() 方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址

public static void test1() { String s1 = "a" + "b" + "c"; // 得到 abc的常量池 String s2 = "abc"; // abc存放在常量池,直接将常量池的地址返回 /** * 最终java编译成.class,再执行.class */ System.out.println(s1 == s2); // true,因为存放在字符串常量池 System.out.println(s1.equals(s2)); // true } public static void test2() { String s1 = "javaEE"; String s2 = "hadoop"; String s3 = "javaEEhadoop"; String s4 = "javaEE" + "hadoop"; String s5 = s1 + "hadoop"; String s6 = "javaEE" + s2; String s7 = s1 + s2; System.out.println(s3 == s4); // true System.out.println(s3 == s5); // false System.out.println(s3 == s6); // false System.out.println(s3 == s7); // false System.out.println(s5 == s6); // false System.out.println(s5 == s7); // false System.out.println(s6 == s7); // false String s8 = s6.intern(); System.out.println(s3 == s8); // true }

从上述的结果我们可以知道:

如果拼接符号的前后出现了变量,则相当于在堆空间中 new String(),具体的内容为拼接的结果

而调用 intern 方法,则会判断字符串常量池中是否存在 JavaEEhadoop 值,如果存在则返回常量池中的值,否者就在常量池中创建

底层原理

拼接操作的底层其实使用了 StringBuilder

【JVM之内存与垃圾回收篇】StringTable

s1 + s2 的执行细节

StringBuilder s = new StringBuilder();

s.append(s1);

s.append(s2);

s.toString(); -> 类似于new String("ab");

在 JDK5 之后,使用的是 StringBuilder,在 JDK5 之前使用的是 StringBuffer

String StringBuffer StringBuilder
String 的值是不可变的,这就导致每次对 String 的操作都会生成新的 String 对象,不仅效率低下,而且浪费大量优先的内存空间   StringBuffer 是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个 StringBuffer 对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量   可变类,速度更快  
不可变   可变   可变  
  线程安全   线程不安全  
  多线程操作字符串   单线程操作字符串  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjgfw.html