jvm—MetaspaceSize与元空间


元空间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之后应保留的最小和最大空闲比例,以调整元空间的动态扩展和收缩行为。

  • MinMetaspaceFreeRatio
    • 指定了在垃圾收集后,元空间应该至少保留的最小空闲空间比例(以百分比表示)。如果元空间的空闲空间低于这个比例,JVM 可能会触发元空间的扩展,以避免因为空间不足导致的频繁 GC 或是 OutOfMemoryError
  • MaxMetaspaceFreeRatio
    • 定义了在垃圾收集后,元空间应该保持的最大空闲空间比例。当空闲空间超过这个比例时,JVM 会尝试收缩元空间,释放不再需要的内存给操作系统
  • 示例  -XX:MinMetaspaceFreeRatio=10  -XX:MaxMetaspaceFreeRatio=50
    • JVM 将确保元空间在GC后至少有10%的空间是空闲的,以防止因为空间紧张引发的GC或内存问题。同时,当元空间的空闲空间超过50%时,会尝试收缩元空间,避免内存浪费


示例: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)