答案在技术上都不是 . 根据Java虚拟机规范,存储字符串文字的区域在runtime constant pool中 . 运行时常量池内存区域是基于每个类或每个接口分配的,因此它根本不依赖于任何对象实例 . 运行时常量池是方法区域的子集"stores per-class structures such as the runtime constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface type initialization" . VM规范说尽管方法区域在逻辑上是堆的一部分,但它并没有规定在方法区域中分配的内存会受到垃圾收集或与分配给堆的普通数据结构相关联的其他行为的影响 .
CLARIFICATION #1 - 我'm not saying that Permgen doesn'得到GC'ed . 它通常在JVM决定运行Full GC时执行 . 我的观点是,只要可以访问使用它们的代码,就可以访问字符串文字,只要代码的类加载器可以访问,代码就可以访问,对于默认的类加载器,这意味着"for ever" .
CLARIFICATION #2 - 事实上,Java 7存储实习生'd String objects in the regular Heap. This includes (I presume) String object that represent String literals. (See @assylias' s答案了解详情 . )
6 回答
答案在技术上都不是 . 根据Java虚拟机规范,存储字符串文字的区域在runtime constant pool中 . 运行时常量池内存区域是基于每个类或每个接口分配的,因此它根本不依赖于任何对象实例 . 运行时常量池是方法区域的子集"stores per-class structures such as the runtime constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface type initialization" . VM规范说尽管方法区域在逻辑上是堆的一部分,但它并没有规定在方法区域中分配的内存会受到垃圾收集或与分配给堆的普通数据结构相关联的其他行为的影响 .
正如this answer所解释的那样,字符串池的确切位置未指定,并且可能因JVM实现而异 .
有趣的是,在Java 7之前,池位于热点JVM上的堆的permgen空间中,但是it has been moved to the main part of the heap since Java 7:
在Java 8 Hotspot中,永久生成已被完全删除 .
字符串文字不存储在堆栈中 .
字符串文字(或更准确地说,代表它们的String对象)历史上存储在称为"permgen"堆的堆中 . (Permgen是永久性的一代 . )
在正常情况下,字符串文字和permgen堆中的许多其他内容都是“永久”可访问的,并且不会被垃圾回收 . (例如,可以从使用它们的代码对象中访问字符串文字 . )但是,您可以配置JVM以尝试查找和收集不再需要的动态加载的类,这可能导致字符串文字被垃圾收集 .
CLARIFICATION #1 - 我'm not saying that Permgen doesn'得到GC'ed . 它通常在JVM决定运行Full GC时执行 . 我的观点是,只要可以访问使用它们的代码,就可以访问字符串文字,只要代码的类加载器可以访问,代码就可以访问,对于默认的类加载器,这意味着"for ever" .
CLARIFICATION #2 - 事实上,Java 7存储实习生'd String objects in the regular Heap. This includes (I presume) String object that represent String literals. (See @assylias' s答案了解详情 . )
String pooling
String.intern() in Java 6
String.intern() in Java 7
String pool values are garbage collected
source.
对于已经包含在这里的伟大答案,我想在我的视角中添加一些缺失的东西 - 和插图 .
因为您已经将JVM将分配的内存分为两部分 . 一个是 stack ,另一个是 heap . 堆栈用于执行目的,堆用于存储目的 . 在该堆内存中,JVM分配了一些专门用于字符串文字的内存 . 这部分堆内存称为 string constants pool .
例如,如果您初始化以下对象:
字符串文字
s1
和s2
将转到字符串常量池,对象obj1,obj2,obj3到堆 . 所有这些都将从Stack中引用 .另请注意,"abc"将出现在堆和字符串常量池中 . 为什么
String s1 = "abc"
和String obj1 = new String("abc")
将以这种方式创建?这是因为String obj1 = new String("abc")
显式创建了一个String对象的新的和引用的 distinct 实例,并且String s1 = "abc"
可以重用字符串常量池中的实例(如果有) . 有关更详细的解释:https://stackoverflow.com/a/3298542/2811258正如其他答案所解释的那样,Java中的内存分为两部分
1. Stack: 每个线程创建一个堆栈,它存储堆栈帧,这些堆栈帧再次存储局部变量,如果变量是引用类型,则该变量引用堆中实际对象的内存位置 .
2. Heap: 将仅在堆中创建所有类型的对象 .
堆内存再次分为3个部分
1. Young Generation: 存储寿命短的对象,Young Generation本身可以分为两类 Eden Space 和 Survivor Space .
2. Old Generation: 存储在许多垃圾收集周期中仍然存在且仍在引用的对象 .
3. Permanent Generation: 存储有关该程序的元数据,例如运行时常量池 .
字符串常量池属于堆内存的永久生成区域 .
正如How Does JVM Handle Method Overloading and Overriding Internally中所讨论的,我们可以通过使用
javap -verbose class_name
来查看字节码中代码的运行时常量池,它将向我们展示方法引用(#Methodref),类对象(#Class),字符串文字(#String)