画图理解Java String的内存分布

2,717 阅读2分钟

文章大纲
  • 创建字符串内存分配图

  • “+”拼接字符串内存分配图


1 创建字符串

众所周知,Java 中有两种字符串的创建方式。


使用双引号创建的字符串,见下面代码:
String test = "ab";

字符串 “ab” 会直接出现在字符串常量池中。
JDK6的版本,常量池在持久代 PermGen 中分配;
JDK7的版本,常量池在堆内存 Heap 中分配;
本文的内存分配图都是基于JDK7以上的版本



而通过 new String() 的方式创建的字符串,如下所示:
String test = new String("ab");

字符串 “ab” 同样会出现在字符串常量池中,同时在堆内存中也会分配一块空间存放字符串 “ab”。


2 "+"拼接字符串

当字符串遇上了 “+” 号,内存的分配情况开始变得奇怪且有趣了。



案例2-1

String test = "ab" + "cd";


案例 2-1 这种情况编译器会直接优化成 “abcd”, 等价于下面代码:

String test = "abcd";



案例2-2

String ab = "ab";
String cd = "cd";
String test = ab + cd; // @1


案例 2-2 中 @1 这里实际上是使用 StringBuilder.append 来完成字符串的拼接, 所以在堆内存中会存在一个 “abcd”, 但是在字符串常量池中是不会有 “abcd” 的。

代码执行完之后,字符串常量池中的 “ab” 和 “cd” 也会保留下来。



案例2-3

final String ab = "ab";
final String cd = "cd";
String test = ab + cd; // @1


案例 2-3 这种情况实际上,因为加上了关键字 final,所以 @1 等价于下面代码,也就是回到了案例 2-1 的情况:

String test = "ab" + "cd";


此时的内存分配图如下,和案例 2-1 还是有点区别的:

案例2-4

String ab = new String("ab"); 
String cd = new String("cd");
String test1 = ab + cd;


案例 2-4 这种情况,具体的内存分配无需多说了,这里值得留意的点是,字符串常量池是不会有 “abcd” 的。


案例2-5

String ab = new String("ab");
String test1 = ab + "cd";


案例 2-5 字符串常量池同样是不会有 “abcd” 的。



总结

到这里,我们就算分析为 “+” 拼接字符串的情况了,虽然内存分配图看起来很复杂,但是总结起来也挺简单的。

只有下面这两种情况,拼接的字符串 “abcd” 才会出现在常量池中:

两个引号 "ab" + "cd"

两个final final + final


你以为这就结束了? 其实正戏才刚刚开始。

当字符串用上了 String#intern 后,难度再次升级!

先容我缓缓,咱们下篇见。


往期回顾

Kafka Topic为什么要分区

一张图理解Kafka时间轮(TimingWheel)

Java队列是怎么支撑起多人运动的?

Java线程池的故事2, 四个花瓶


记得关注公众号"字节武装"哦!!!