String 之字符串与内存管理

来源:互联网 发布:淘宝怎么看卖家信誉度 编辑:程序博客网 时间:2024/05/16 13:38

GitHub 的阅读效果更佳

字符串涉及到的所有空间在内存中存在于三个位置:方法区的常量池堆中的 String 类型对象虚拟机栈中 String 对象引用

new String 与 “hello”

String a = new String("hello ") 是创建对象的过程,String 对象创建过程与其他对象创建的过程大致相同。但是在<init>阶段,会从常量池中寻找 “hello” 常量,若成功找到,则复制一份到堆中,构成 String 类型对象的一部分;否则,在堆中自行创建 “hello” 字符串,用于对 String 类中的属性进行赋值。

因此,无论怎么样,创建对象时,字符串一定存在于堆中,a 引用的指向是堆中的字符串,而不是常量池中的常量

String b = "hello " 先创建一个栈引用,然后从常量池中进行常量寻找(因为在 JVM 中,常量是一种资源,应当合理的重复使用),如果找到,则将引用指向它;否则,在常量池中创建新的常量 “hello”,然后进行引用指向。

字符串的连接

字符串的连接分为两种:编译期间的连接运行期间的链接

编译期间的连接

final String a = "hello ";final String b = "world";String c = a + b;

上述代码的执行过程为:

  1. 因为 a 与 b 都是常量,在编译器中 a 就是 “hello”(同理,b 就是 “world”),两者无论使用哪个都是指的一个东西,所以在在编译的时候(要注意,这时还没有进行内存分配,字符串常量池还没有被初始化,所有的工作都停留在字节码层面)代码变成了这样:

    final String a = "hello ";final String b = "world";String c = "hello" + "world";
  2. 拼接静态字符串的时候,编译器通常要进行进行优化,对 c 中字符串的拼接进行优化:

    String c = "hello" + "world";// 编译器将视为String c = "hello world";
  3. 最后,等到内存分配阶段,字符串常量池中会有:”hello” “world” “hello world” 三个常量的存在。

运行期间的链接

final String constant_hello = "hello ";final String constant_world = "world";string var_hello = "hello ";String concatenate_c = constant_hello + constant_world;String concatenate_f = var_hello + constant_world;System.out.println(concatenate_c == concatenate_f); // ?会相等吗,false

为什么 concatenate_c 和 concatenate_f 不相等呢?

我们知道,concatenate_c 在经过编译器的处理与优化过后,会变成:

concatenate_c = "hello world";

原因是 constant_hello 和 constant_world 为常量,编译器能够认识他们。而 var_hello 并非常量,编译器不认识它,所以会变成这样:

String concatenate_f = var_hello + "world";

在内存分配的时候,才会得知 var_hello 的值,这时候不会再做静态的拼接,而是进行动态的链接

concatenate_f --> "hello " --> "world"

concatenate_c == concatenate_f 自然不成立了。

字符串相等不想等(而非 equals()),这是一个很有意思的问题,既要考虑编译器的优化,还要考虑字符串内存的分配与管理

参考资料:

  • IBM - Java 性能优化之 String 篇
  • 《深入理解 JVM 虚拟机》
0 0