方法内声明的字符串变量内存分配位置:栈还是堆?
方法内字符串变量的内存分配详解
嘿,这俩问题其实是一套逻辑,我给你掰扯清楚~
1. 方法内部字符串变量的内存分配位置
方法里的字符串变量得分成两部分来看:
- 引用变量本身:比如你写的
String str;或者String str = "hello";里的str,这个变量是存在当前方法的**栈帧(Stack Frame)**里的,属于栈内存区域。等方法执行完毕,栈帧弹出,这个引用就会被销毁。 - 字符串的实际对象内容:这部分的位置分两种场景:
- 如果是字面量声明(比如
String str = "hello";):JVM会先去字符串常量池(Java 7及之后,这个池是堆内存里的一块特殊区域)找有没有"hello",有的话直接让引用指向它;没有的话就先在常量池里创建这个字符串对象,再让引用指向它。 - 如果是new关键字创建(比如
String str = new String("hello");):JVM会先检查常量池里有没有"hello",没有就先在常量池创建,然后再在堆的普通区域生成一个新的字符串对象,最后让引用指向这个堆里的新对象。
- 如果是字面量声明(比如
2. 方法内字符串变量:栈还是堆?
这个问题得拆开来回答:
- 变量的引用是在栈里的——因为它是方法内的局部变量,属于栈帧的局部变量表,方法一结束就跟着栈帧一起回收。
- 字符串的实际对象实例(不管是常量池里的还是new出来的)都是在堆内存里的——哪怕是字符串常量池,从Java 7开始也已经移到堆里了,之前的方法区(永久代)现在改成元空间,但常量池还是归属堆区域。
举个代码例子更直观:
public void testStringMemory() { // str1是栈里的引用,指向堆中常量池里的"hi" String str1 = "hi"; // str2是栈里的引用,指向堆普通区域的新String对象,该对象内部会指向常量池的"hi" String str2 = new String("hi"); }
执行这个方法时,str1和str2这两个引用都待在testStringMemory方法的栈帧里,而它们指向的字符串实体全在堆内存,只是所在的子区域不同而已。
额外补充个小细节:如果是字面量拼接(比如String str = "a" + "b";),编译器会直接优化成"ab",所以引用还是指向常量池里的对象;但如果是变量拼接(比如String str = str1 + str2;),会在堆的普通区域生成新对象(除非拼接结果刚好在常量池里)。
内容的提问来源于stack exchange,提问作者Sujeet Kumar
相关产品推荐
相关产品推荐

