运行时常量池随类加载动态增长,不占满Metaspace;它存储符号引用等元信息,不存对象,且不可被GC回收,仅随类卸载释放。
运行时常量池不是JVM启动时就填满的
很多人以为
启动后,方法区里的运行时常量池(Runtime Constant Pool)就立刻占掉一大块内存——其实不是。它随类加载动态增长,每个类的常量池在
阶段才被解析并写入,而且只存符号引用、字面量、方法句柄等元信息,不存实际对象。
常见错误现象:
经常被误判为“常量池爆了”,其实更可能是重复定义类(如热部署未清理旧
)或大量动态生成类(
、
),而非字符串常量塞太多。
字符串字面量(如
)首次使用时才进入常量池,且 JDK 7+ 后存于堆中,不占方法区
返回的新对象不会进常量池,只有
范围缓存才复用
用
查看某个 class 的常量池内容,比猜更有依据
运行时常量池和字符串池(String Table)是两回事
环境配置
里最常混淆的点:改
只影响字符串驻留表(
)的哈希桶数量,跟运行时常量池完全无关。后者大小由类结构决定,无法直接配置参数控制。
使用场景:当你在排查
调用变慢,或发现
占用过高(
中
值异常),别去调
,该调的是
(默认 60013,质数)。
立即学习
“
Java免费学习笔记(深入)
”;
JDK 8 默认
是 60013;JDK 11+ 改为可动态扩容,但初始值仍需合理设置
在 JDK 7+ 后操作的是堆内字符串池,不是方法区里的运行时常量池
运行时常量池里存的是
结构,不是 Java 对象,不能被 GC 回收
GC 对运行时常量池的影响很有限
运行时常量池属于方法区的一部分,而方法区在 JDK 8+ 是 Metaspace,底层用本地内存管理。这意味着:常量池里的符号引用不会因为堆 GC 就消失,也不会被 CMS 或 G1 的常规 GC 扫描清理。
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
真正能释放常量池空间的,只有类卸载(class unloading)——前提是该类的
本身被回收,且无任何强引用指向该类或其实例。这在 Web 容器(如 Tomcat)热部署时极难满足。
开启
可观察哪些类被真正卸载,别光看
使用率下降就以为常量池清了
不影响常量池大小,但它压缩的是类元数据指针,间接减少 Metaspace 总用量
频繁
不会填满运行时常量池,但可能撑爆
(尤其小
时哈希冲突严重)
验证运行时常量池状态得用对工具
靠
看不到运行时常量池细节,它只显示 Metaspace 整体用量。想确认某个类的常量池是否加载、有没有重复项,必须结合静态分析和运行时探测。
性能影响:反复反射调用
会触发常量池中符号引用解析,如果类没预热,首次调用有微小延迟;但解析结果会被缓存,后续无额外开销。
用
查看编译期常量池结构
用
观察 Metaspace 实际内存分布,比
更准
日志里出现
才说明对应常量池空间被回收
真正难处理的不是常量池大小本身,而是类加载器生命周期和符号引用解析时机之间的耦合——这点在模块化(JPMS)或 OSGi 场景下尤其容易出问题,但 JVM 参数层面根本没法“一键修复”。
java -XX:MaxMetaspaceSize=256mClassLoader.defineClass()OutOfMemoryError: MetaspaceClassLoaderCGLIBASM"hello"Integer.valueOf(128)-128 ~ 127javap -v-XX:StringTableSizeStringTableintern()StringTablejstat -gcCCSCMaxMetaspaceSize-XX:StringTableSize=65536StringTableSizeString.intern()CONSTANT_Utf8_infoClassLoader-XX:+TraceClassUnloadingMetaspace-XX:+UseCompressedClassPointersnew String("abc").intern()StringTableStringTableSizejstat -gcMethod.invoke()javap -v MyClass.class | grep "Constant pool"jcmd VM.native_memory summary scale=MB jstat-XX:+PrintGCDetailsUnloading class net.example.MyHandler