一、分析问题
二、优化操作
优化一: 元空间大小动态调整
- 元空间的大小不必是固定的,JVM 可以根据应用的需要动态调整。
- 默认值依赖于平台,Windows 下,
-XX:MetaspaceSize 约为 21M
,-XX:MaxMetaspaceSize 的值是-1 即没有限制
。 - 与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。如果元数据区发生溢出,虚拟机一样会抛出异常
OutOfMemoryError:Metaspace
。 -XX:MetaspaceSize
:设置初始的元空间大小。- 对于一个 64 位 的服务器端 JVM 来说,其默认的
-XX:MetaspaceSize 值为 21MB。这就是初始的高水位线
, - 一旦触及这个水位线,
Full GC 将会被触发并卸载没用的类
(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。 - 新的高水位线的值取决于 GC 后释放了多少元空间。
- 如果
释放的空间不足
,那么在不超过 MaxMetaspaceSize 时,适当提高该值
。 - 如果
释放空间过多
,则适当降低该值
。
- 对于一个 64 位 的服务器端 JVM 来说,其默认的
- 如果初始化的高水位线
设置过低
,上述高水位线调整情况会发生很多次。- 通过垃圾回收器的日志可以观察到 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);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
- 输出如下异常信息:
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)
1
2
3
4
5
6
2
3
4
5
6
在 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 异常可能需要根据具体情况进行调试和优化。建议在进行任何调整之前,先进行充分的测试和验证,确保不会引入其他问题。