Skip to content

一、分析问题

二、优化操作

优化一: 元空间大小动态调整

  • 元空间的大小不必是固定的,JVM 可以根据应用的需要动态调整。
  • 默认值依赖于平台,Windows 下,-XX:MetaspaceSize 约为 21M-XX:MaxMetaspaceSize 的值是-1 即没有限制
  • 与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。如果元数据区发生溢出,虚拟机一样会抛出异常 OutOfMemoryError:Metaspace
  • -XX:MetaspaceSize:设置初始的元空间大小。
    • 对于一个 64 位 的服务器端 JVM 来说,其默认的 -XX:MetaspaceSize 值为 21MB。这就是初始的高水位线
    • 一旦触及这个水位线,Full GC 将会被触发并卸载没用的类(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。
    • 新的高水位线的值取决于 GC 后释放了多少元空间。
    • 如果释放的空间不足,那么在不超过 MaxMetaspaceSize 时,适当提高该值
    • 如果释放空间过多,则适当降低该值
  • 如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。
    • 通过垃圾回收器的日志可以观察到 Full GC 多次调用。
    • 为了避免频繁地 GC,建议将-XX:MetaspaceSize 设置为一个相对较高的值

优化二: 解决元空间 OOM 异常

  • 继承 ClassLoader 类,获得 defineClass() 方法,可自己进行类的加载。
java
package com.calvin.jvm.structure.metaspace.question.example;

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;

/**
 * 元空间Oom问题
 *
 *
 * @author calvin
 * @date 2023/10/07
 */
public class MetaspaceOomQuestion extends ClassLoader {

    /**
     * jdk6/7中:
     *  -XX:PermSize=10m -XX:MaxPermSize=10m
     * <p>
     * jdk8中:
     *  -XX:-UseCompressedClassPointers -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
     *
     * @param args
     */
    public static void main(String[] args) {
        int j = 0;
        try {
            MetaspaceOomQuestion test = new MetaspaceOomQuestion();
            for (int i = 0; i < 10000; i++) {
                // 创建ClassWriter对象,用于生成类的二进制字节码
                ClassWriter classWriter = new ClassWriter(0);
                // 指明版本号,修饰符,类名,包名,父类,接口
                classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                // 返回byte[]
                byte[] code = classWriter.toByteArray();
                // 类的加载 (自定义类加载)
                test.defineClass("Class" + i, code, 0, code.length);
                j++;
            }
        } finally {
            System.out.println(j);
        }
    }
}
  • 输出如下异常信息:
log
9344
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:636)
	at com.calvin.jvm.structure.metaspace.question.example.MetaspaceOomQuestion.main(MetaspaceOomQuestion.java:37)

在 Java 中,方法区 Java 中元空间(Metaspace)OOM 异常通常是由于元空间中加载的类和元数据过多导致的。元空间是 Java 8 及其后续版本中取代了永久代(PermGen)的存储区域,用于存储类的元数据信息。

要解决元空间 OOM 异常,可以尝试以下几种方法:

  • 增加元空间的大小:通过调整 JVM 的启动参数,增加元空间的最大大小。
    • 可以使用"-XX:MaxMetaspaceSize"参数来指定最大元空间的大小。
    • 例如:"-XX:MaxMetaspaceSize=256m"表示最大元空间大小为 256M。
  • 优化类的加载和卸载: 检查应用程序的类加载和卸载情况,确保只加载必要的类,并及时卸载不再使用的类。
    • 可以使用工具来监视类的加载和卸载情况
    • 例如 VisualVM 或 JConsole。
  • 检查内存泄漏:元空间 OOM 异常有时也可能是由于内存泄漏导致的。
    • 检查应用程序的代码,确保没有存在内存泄漏的情况。
    • 可以使用内存分析工具
    • 例如 Eclipse Memory Analyzer(MAT)YourKit 来帮助检测和解决内存泄漏问题。
  • 调整其他内存参数:除了元空间大小外,还可以调整其他与内存相关的参数,
    • 例如堆内存大小(-Xmx 和-Xms 参数)、垃圾回收器的选择等。
    • 根据应用程序的需求和特点,适当调整这些参数可能有助于解决 OOM 异常。

请注意,解决元空间 OOM 异常可能需要根据具体情况进行调试和优化。建议在进行任何调整之前,先进行充分的测试和验证,确保不会引入其他问题。