元空间Metaspace 是Java 8及之后版本中用来替代永久代(PermGen)的内存区域,用于存储类元数据,包括类的结构信息(如字段、方法、常量池等)、类加载器的元数据以及方法的即时编译代码(JIT 编译后的代码缓存)等。与永久代不同,Metaspace 使用的是本机内存(Native Memory),而不是JVM堆内存。这意味着它不再受JVM堆大小的直接限制,减少了因永久代大小配置不当导致的内存问题。
Metaspace的大小可以根据需要动态扩展,直到达到操作系统或JVM配置的最大限制。这提高了灵活性,减少了内存管理的预配置工作。
在Metaspace中,每个类加载器都有自己的内存空间,这意味着不同加载器加载的类不会共享元数据空间,有利于垃圾回收和内存管理。当一个类加载器不再被使用且可以被垃圾回收时,其占用的Metaspace也会随之回收,由于Metaspace使用本地内存,避免了过去永久代中常见的java.lang.OutOfMemoryError: PermGen space错误。不过,如果Metaspace达到配置的上限,仍然会抛出java.lang.OutOfMemoryError: Metaspace错误。
MetaspaceSize 参数用于设置元空间的初始分配大小。当JVM启动或类加载开始时,元空间会根据此参数设定的大小进行初始化分配。如果未显式设置,JVM会使用默认值。适当设置MetaspaceSize可以帮助避免因元空间不足导致的频繁垃圾回收或内存溢出问题。如果应用程序在启动时或短时间内需要加载大量类,增大MetaspaceSize可以减少因元空间扩展而导致的停顿。
MaxMetaspaceSize 参数用于限制元空间的增长,以防止耗尽系统资源导致的问题,如内存溢出。如果不设置MaxMetaspaceSize,JVM会根据系统内存状况动态调整元空间的大小,直到达到操作系统内存限制或发生内存压力。如果元空间达到MaxMetaspaceSize所设定的上限,JVM将会抛出java.lang.OutOfMemoryError: Metaspace错误,表明无法再分配更多的元数据空间给新的类或类加载器。
MinMetaspaceFreeRatio 和 MaxMetaspaceFreeRatio:这两个参数分别控制了元空间在GC之后应保留的最小和最大空闲比例,以调整元空间的动态扩展和收缩行为。
示例:MaxMetaspace
public class MaxMetaspace {
public static void main(String[] args) throws Exception {
try {
List<ClassLoader> loaders = new ArrayList<>();
int counter = 1;
while (true) {
loaders.add(DynamicClassLoader.ClassLoader("GeneratedClass"+(counter++))) ;
Thread.sleep(1);//避免加载过快
}
} catch (Exception e) {
System.err.println(e);
}
}
}
执行:
java -XX:MetaspaceSize=50m -XX:MaxMetaspaceSize=50m -classpath ./classes;./repository/org/ow2/asm/asm/9.4/asm-9.4.jar io.donnie4w.jvm.MaxMetaspace
说明:程序依赖asm动态创建类并加载到元空间,因此需要第三方包asm-9.4.jar的支持
以下是元空间增长与类加载持续增加的截图
执行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:889)
at io.donnie4w.jvm.DynamicClassLoader$1.findClass(DynamicClassLoader.java:18)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at io.donnie4w.jvm.DynamicClassLoader.ClassLoader(DynamicClassLoader.java:21)
at io.donnie4w.jvm.MaxMetaspace.main(MaxMetaspace.java:20)