字符串常量池和运行时常量池是在堆还是在方法区?


        我们知道在JDK1.8中取消了永久代,区而代之使用了元空间来实现方法区。话虽如此,但是关于字符串常量池和运行时常量池的模棱两可的说法一直都是争论不休的。

1)方法区包含哪些内容?

方法区包含哪些内容,摘录自《java虚拟机规范-第8版》:
在这里插入图片描述
方法区包含:
        运行时常量池
        自动和方法数据
        构造函数和普通方法的字节码内容
        一些特殊方法
这里虽然没有说明“字符串常量池”,但是它也是方法区的一部分。

2)运行时常量池存在什么地方?

下面是《深入理解Java虚拟机》一段摘录:
在这里插入图片描述
能够看到
        运行时常量池是在方法区中
        对于运行时常量池,《Java虚拟机规范》并没有做任何细节的要求,不同提供商实现的虚拟机可以按照自己的需要来实现这个内存区域

3)取消永久代后,方法区的实现?

在这里插入图片描述

取消永久代后,使用元空间来实现方法区。
        在JDK1.8中,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。注意这里的剩余内容:说明原来移除从永久代移出的字符串常量池,静态常量,在更换了方法区实现后,并没有顺势进入到元空间,那么它们到哪里去了呢?

4)字符串常量池和运行时常量池究竟去了哪里?

        在JDK1.8中,使用元空间代替永久代来实现方法区,但是方法区并没有改变,所谓"Your father will always be your father",变动的只是方法区中内容的物理存放位置。正如上面所说,类型信息(元数据信息)等其他信息被移动到了元空间中;但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区的。

        JDK1.8中字符串常量池和运行时常量池逻辑上属于方法区,但是实际存放在堆内存中,因此既可以说两者存放在堆中,也可以说两则存在于方法区中,这就是造成误解的地方。

        关于佐证运行常量池和字符串常量池被移动到了堆中,可以参考这个博客:https://mp.weixin.qq.com/s__biz=MzI4NDY5Mjc1Mg==&mid=2247485613&idx=1&sn=b2b1679033d24e965a3dd73dfab6dfaa&chksm=ebf6d0d2dc8159c4ae291d99e9c337b0cdb05578ecf7bb43671ad02cdbecfdf916a98ab18929&scene=27#wechat_redirect

        其实,移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。我们可以通过一段程序来比较 JDK 1.6 与 JDK 1.7及 JDK 1.8 的区别,以字符串常量为例:

package com.paddx.test.memory;

import java.util.ArrayList;
import java.util.List;

public class StringOomMock {
    static String  base = "string";
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i=0;i< Integer.MAX_VALUE;i++){
            String str = base + base;
            base = str;
            list.add(str.intern());
        }
    }
}

        这段程序以2的指数级不断的生成新的字符串,这样可以比较快速的消耗内存。我们通过 JDK 1.6、JDK 1.7 和 JDK 1.8 分别运行:
在这里插入图片描述
在这里插入图片描述
        从上述结果可以看出,JDK 1.6下,会出现“PermGen Space”的内存溢出,而在 JDK 1.7和 JDK 1.8 中,会出现堆内存溢出,并且 JDK 1.8中 PermSize 和 MaxPermGen 已经无效。因此,可以大致验证 JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中

4)元空间是什么?

        元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

        -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

        -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

        -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集.

        -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

        现在我们在 JDK 8下重新运行一下代码段 4,不过这次不再指定 PermSize 和 MaxPermSize。而是指定 MetaSpaceSize 和 MaxMetaSpaceSize的大小。输出结果如下:

在这里插入图片描述

5)关于为什么移除永久代?

        字符串存在永久代中,容易出现性能问题和内存溢出。
        类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
        永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
        Oracle 可能会将HotSpot 与 JRockit 合二为一。

5)补充

某种程度上说,该图也是正确的:来源:
https://www.zhihu.com/question/300075241/answer/519570351
在这里插入图片描述

  • 40
    点赞
  • 154
    收藏
    觉得还不错? 一键收藏
  • 25
    评论
### 回答1: 字符串常量池JVM中属于方法(也称为永久代)内存分方法是各个线程共享的内存域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。字符串常量池方法中的一部分,用于存储字符串常量。在JDK 8之后,方法被取消了,取而代之的是元空间(MetaSpace),但字符串常量池仍然存放在元空间中。 ### 回答2: 字符串常量池JVM方法(也称为非)中。 JVM将内存分为几个不同的域,包括方法、虚拟机栈等。而字符串常量池方法的一部分,用于存储在程序中直接使用的字符串常量。 在Java中,字符串常量池是一种特殊的内存存储域,用于存储字符串常量,它的作用是提高字符串的重用性和效率。当我们使用双引号声明一个字符串时,JVM会首先在字符串常量池中查找是否存在相同内容的字符串,如果存在则直接返回引用,如果不存在则创建一个新的字符串并放入字符串常量池中。这种机制可以减少内存占用,提高程序的执行效率。 由于字符串常量池位于方法,它是与其他线程共享的,在程序运行过程中,多个线程可以同时访问字符串常量池。而且,字符串常量池的位置是在程序的执行过程中被动态调整的,当字符串没有被引用时,JVM会自动回收字符串常量池中的空间。 总结来说,字符串常量池JVM方法的一部分,用于存储程序中直接使用的字符串常量,并提高字符串的重用性和效率。 ### 回答3: 字符串常量池JVM方法里。方法JVM的一个内存分,用于存储类信息、常量、静态变量、即时编译器编译后的代码等。而字符串常量池就是方法的一部分,用于存储字符串常量。 在Java中,当我们使用字符串字面量(如"hello")时,编译器会将其放入字符串常量池中。当程序执行时,如果再次使用相同的字符串字面量,JVM会直接从字符串常量池中取出已存在的字符串对象,而不会创建新的对象,这样可以节省内存空间。 由于字符串在Java中使用非常频繁,所以将字符串常量池放在方法中,可以提高字符串的重用率。此外,字符串常量池的位置在方法中也有利于GC(垃圾回收),因为当某个字符串不再被引用时,GC可以更方便地回收该字符串常量。 需要注意的是,从Java 7开始,字符串常量池被移出了PermGen空间(方法的前身),并放置在中,这是因为字符串常量池中的字符串对象是可以被垃圾回收的,而且过多的字符串常量可能导致PermGen空间溢出的问题。
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值