java常量池在方法区还是堆_JAVA常量池

java常量池在方法区还是堆_JAVA常量池要是没有实践过别人书本上的理论的话,就还是会说常量池在方法区里面,要是知道方法区已经随jdk升级,被逐步干掉的话,就会看到有的文章说移动到heap堆里面了,还有极少的说移动到Metaspace里面了,产生了分歧。这个时候就需要实践出真知了。/***测试常量池在分区的位置**@authorLiXuekaion2020/6/9*/publicclassStringConstantPoolTest{publicstaticvoidmain(String[]

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

要是没有实践过别人书本上的理论的话,就还是会说常量池在方法区里面,要是知道方法区已经随jdk升级,被逐步干掉的话,额,也不能说被干掉,只是被优化了,这又体现了看书的程度深浅了,就会看到有的文章说常量池移动到heap堆里面了,还有极少的说移动到Metaspace里面了,产生了分歧。这个时候就需要实践出真知了。

/**
 * 测试 常量池在分区的位置
 *
 * @author LiXuekai on 2020/6/9
 */
public class StringConstantPoolTest {
    public static void main(String[] args) {
        List<String> list = Lists.newArrayList();
        while (true) {
            list.add(String.valueOf(System.currentTimeMillis()).intern());

        }
    }
}

代码很简单,那本书上也大致是这个样子。String.intern()会将字符串丢到字符串常量池里面。以此来不断增加常量池的使用部分。

还有你得一直使用着这个字符串,不然就被gc了,你就看不到oom现象了。

jdk1.6的测试

他当时测试的时候,出的异常是方法区OOM.

java常量池在方法区还是堆_JAVA常量池

当时的jdk还是1.6,我这就不测试1.6的了。

执行结果说明jdk1.6的时候常量池在方法区。

jdk1.7的测试

下面是jdk1.7的测试情况,还是这个代码,就是启动参数设置的不一样。

使用的jvm参数设置:

-XX:+PrintGCDetails -Xms100M -Xmx100M -Xmn10M -XX:SurvivorRatio=8 -XX:PermSize=10m -XX:MaxPermSize=10m

然后这个程序的异常截图如下:

java常量池在方法区还是堆_JAVA常量池

堆空间溢出。

使用jvm看内存分区的使用情况的截图:

java常量池在方法区还是堆_JAVA常量池

可以看到堆里面old区总共90M,已经89M,这个是在报oom之前的截图。方法区总共就分了10m,在oom的时候,也就使用了7m多,说明这个常量池,在jdk1.7的时候,确实被安排到了堆Java heap里面了。

上面的说明加起来,使得下面这个理论得到了验证。

永久代主要存放以下数据:

JVM internal representation of classes and their metadata //类及其元数据的JVM内部表示
Class statics //类的静态
Interned strings //实际字符串,说的就是常量池吧

从 JDK7 开始,JDK 开发者们就有消灭永久代的打算了。有部分数据移到永久代之外了:

Symbols => native memory // 符号引用 >本机内存
Interned strings => Java Heap // Interned string => Java堆
Class statics => Java Heap //类statics => Java堆

结论:jdk1.7的时候,常量池已经被安排在堆里面了。

jdk1.8的测试

使用的jvm参数设置:

-XX:+PrintGCDetails -Xms200M -Xmx200M -Xmn10M -XX:SurvivorRatio=8 -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:CompressedClassSpaceSize=5M

代码执行时候报的异常截图

java常量池在方法区还是堆_JAVA常量池

使用jvm看内存分区的使用情况的截图:

java常量池在方法区还是堆_JAVA常量池

设置堆的大小是200m,元空间就只有10m,测试了几次了,每次堆的old区里面也就残留81m,看元空间的最大是10m,使用了8.4m之后,差不多就oom了。堆还差得远呢。

说明这个常量池就是在元空间里面了吧,不能在Java heap里面了吧。

在看元空间的时候,有个这个图

java常量池在方法区还是堆_JAVA常量池

说是开发jvm的人画的。元空间被分为2部分,类空间 class space 和非类空间 non class space

深入 Class Space:

最大的一部分是 Klass 结构,它是固定大小的。

然后紧跟着两个可变大小的 vtable 和 itable,前者由类中方法的数量决定,后者由这个类所实现接口的方法数量决定。

随后是一个 map,记录了类中引用的 Java 对象的地址,尽管该结构一般都很小,不过也是可变的。

vtable 和 itable 通常也很小,但是对于一些巨大的类,它们也可以很大,一个有 30000 个方法的类,vtable 的大小会达到 240k,如果类派生自一个拥有 30000 个方法的接口,也是同理。但是这些都是测试案例,除了自动生成代码,你从来不会看到这样的类。

深入 Non-Class Space

这个区域有很多的东西,下面这些占用了最多的空间:

  • 常量池,可变大小;
  • 每个成员方法的 metadata:ConstMethod 结构,包含了好几个可变大小的内部结构,如方法字节码、局部变量表、异常表、参数信息、方法签名等;

最后,那就按照老外的说法来吧,我的测试也稍微辅助说明一下吧,虽然代码里面报错是Java heap 溢出。这点奇怪了。

姑且最终结论,常量池就是在Metaspace 元空间里面。

参考:

老外原文地址

怎么想怎么都觉得不对,这个常量池怎么能在元空间里面。

java常量池在方法区还是堆_JAVA常量池

后面又调整参数,-xms 和-xmx设置2g,代码运行时间就更久了,说明啥,数据对象分配了那么多,要是这个方法区,早就被撑爆炸了,怎么可能会在元空间呢。这地方就分给他10,这个图上最大也就13m,肯定不可能装下那么多的字符串的。那老外画的图又作何解释呢。老外画错了,或者说老外没画明白。

所以,最终的测试结论:

结论:这个常量池(特指字符串常量池而不是所有的常量池),应该还是在Java heap里面,

上面的测试只能证明:jdk1.8中 字符串常量池是在堆里面。

JAVA的三种常量池

此外,Java有三种常量池,即字符串常量池(又叫全局字符串池)、class文件常量池、运行时常量池

1. 字符串常量池(也叫全局字符串池、string pool、string literal pool)

字符串常量池   Interned Strings

2. 运行时常量池(runtime constant pool)

当程序运行到某个类时,class文件中的信息就会被解析到内存的方法区里的运行时常量池中。每个类都有一个运行时常量池

3. class文件常量池(class constant pool)

class常量池是在编译后每个class文件都有的,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是 常量池*(constant pool table)*,用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。*字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。

按照这个分析的话,

运行时常量池和静态常量池存放在元空间中,而字符串常量池依然存放在堆中。

这个理论还说的通,但是另外2个常量池就不好测试了。

Java Language and Virtual Machine Specifications

上面这个是jdk的官网链接,里面不管是jdk几,里面都有Method Area方法区

都是这么几个分区,说明这几个名字都是概念。

java常量池在方法区还是堆_JAVA常量池

顺便再上传一下他原文和网页简单翻译的图

java常量池在方法区还是堆_JAVA常量池

java常量池在方法区还是堆_JAVA常量池

java常量池在方法区还是堆_JAVA常量池

java常量池在方法区还是堆_JAVA常量池

Each run-time constant pool is allocated from the Java Virtual Machine’s method area (§2.5.4). The run-time constant pool for a class or interface is constructed when the class or interface is created (§5.3) by the Java Virtual Machine.

方法区确实是一个概念,概念,概念。

干掉的是永久代,而不是方法区。

卧槽,接口还在,只是实现类变了

最开始的实现叫PermGen,后来是PermGen + java heap 一起实现,现在叫Metaspace + Java heap 一起协调工作。

再补充帮助理解。

元空间并不是方法区!!!

方法区包括类信息、常量、静态变量等,是JVM规范。 方法区是jvm规范里面的概念。

1.7之前方法区的实现就是永久代。 1.7 把常量池和静态变量放入了堆中,也就是方法区由永久代和堆实现

1.8 把永久代删除使用元空间,也就是方法区由元空间(类信息)和堆实现(常量池、静态变量)

堆中包含正常对象和常量池,new String()放入堆中,String::inter会将堆中的String变量放入堆中的常量池中。

这个解释就比较完美了。

Each run-time constant pool is allocated from the Java Virtual Machine’s method area (§2.5.4). The run-time constant pool for a class or interface is constructed when the class or interface is created (§5.3) by the Java Virtual Machine.

这句话,可不就是说,所有的运行时常量池都是从jvm的 method area 方法区分配来的。

所以,说常量池在方法区,是对的。因为方法区是个概念的东西。

在jvm里面具体实现还的根据不同的jdk,实现的区的名称也不一样,前有PermGen,后有permGen + Java heap ,后再有metaspace + Java heap。

最终结论:

字符串常量在堆内存,类的元数据在本地内存。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/164776.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(7)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号