概述
JDK8 废弃永久代
永久代(PermGen)
《Java虚拟机规范》定义的JVM内存结构如下图:
关于方法区和永久代:
在HotSpot JVM中,这次讨论的永久代,就是上图的方法区(JVM规范中称为方法区)。《Java虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。在其他JVM上不存在永久代。
JDK8 永久代变化
-
新生代:Eden+From Survivor+To Survivor
-
老年代:OldGen
-
永久代(方法区的实现) : PermGen----->替换为Metaspace(本地内存中)
为何废弃永久代
jdk8之前有perm这一整块内存来存klass等信息,我们的参数里也必不可少地会配置-XX:PermSize
以及-XX:MaxPermSize
来控制这块内存的大小,jvm在启动的时候会根据这些配置来分配一块连续的内存块,但是随着动态类加载的情况越来越多,这块内存我们变得不太可控,到底设置多大合适是每个开发者要考虑的问题,如果设置太小了,系统运行过程中就容易出现内存溢出,设置大了又总感觉浪费,尽管不会实质分配这么大的物理内存。基于这么一个可能的原因,于是metaspace出现了,希望内存的管理不再受到限制,也不要怎么关注元数据这块的OOM问题。
深入理解元空间(metaspace)
元空间的内存大小
元空间是方法区在HotSpot JVM
中的实现,方法区主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。理论上取决于32位/64位系统可虚拟的内存大小,可见也不是无限制的,需要配置参数。
常用配置参数
-
MetaspaceSize
初始化的Metaspace大小,控制元空间发生GC的阈值。GC后,动态增加或降低MetaspaceSize。在默认情况下,这个值大小根据不同的平台在12M到20M浮动。使用jinfo -flag MetaspaceSize <pid>
可以查看该参数的值 -
MaxMetaspaceSize
这个参数会限制metaspace(包括了Klass Metaspace以及NoKlass Metaspace)被committed的内存大小,会保证committed的内存不会超过这个值,一旦超过就会触发GC,这里要注意和MaxPermSize的区别,MaxMetaspaceSize并不会在jvm启动的时候分配一块这么大的内存出来,而MaxPermSize是会分配一块这么大的内存的。 -
MinMetaspaceFreeRatio
当进行过Metaspace GC
之后,会计算当前Metaspace
的空闲空间比,如果空闲比小于这个参数(即实际非空闲占比过大,内存不够用),那么虚拟机将增长Metaspace
的大小。默认值为40,也就是40%。设置该参数可以控制Metaspace
的增长的速度,太小的值会导致Metaspace
增长的缓慢,Metaspace
的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace
增长的过快,浪费内存。 -
MaxMetaspaceFreeRatio
当进行过Metaspace GC
之后, 会计算当前Metaspace
的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace
的部分空间。默认值为70,也就是70%。 -
MaxMetaspaceExpansion: Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。
-
MinMetaspaceExpansion: Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约330KB为)。
metaspace容量
默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)。
新参数(MaxMetaspaceSize)用于限制本地内存分配给类元数据的大小。如果没有指定这个参数,元空间会在运行时根据需要动态调整。
metaspace垃圾回收
对于僵死的类及类加载器的垃圾回收将在元数据使用达到MaxMetaspaceSize
参数的设定值时进行, 进行的是full gc
。
适时地监控和调整元空间对于减小垃圾回收频率和减少延时是很有必要的。持续的元空间垃圾回收说明,可能存在类、类加载器导致的内存泄漏或是大小设置不合适。
测试并追踪元空间大小
测试字符串常量
public class AppTest {
static String base = "string";
public static void main(String[] args) throws InterruptedException {
List<String> list = Lists.newArrayList();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String s = base + base;
base = s;
list.add(s.intern());
}
}
}
执行参数
-Xms20m -Xmx20m -XX:PermSize=8m -XX:MaxPermSize=8m
由于设定了最大内存20M,很快就溢出,如下:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=8m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=8m; support was removed in 8.0
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.jonas.AppTest.main(AppTest.java:17)
可见在jdk8中:
-
字符串常量由永久代转移到堆中,因为字符串存在永久代中,容易出现性能问题和内存溢出。
-
持久代已不存在,PermSize 和 MaxPermSize 参数已移除。
测试元数据空间
当使用到CGLIB
这类字节码技术时,当增强的类越多,就需要越大的方法区以保证动态生成的新类型可以载入内存,否则可能造成方法区溢出。
/**
* 设置最大元数据空间为10M
* 执行参数 -XX:MaxMetaspaceSize=10m
*/
public class App {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(objects, args);
}
});
enhancer.create();
}
}
static class OOMObject {}
}
在JDK8中的运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at java.lang.Class.forName0(Native Method)
......
参考资料
JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)
面试官,Java8 JVM内存结构变了,永久代到元空间
最后
以上就是淡淡抽屉为你收集整理的【JVM】元数据空间(Metaspace)的全部内容,希望文章能够帮你解决【JVM】元数据空间(Metaspace)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复