一 代码
/**
* jdk6/7中:
* -XX:PermSize=10m -XX:MaxPermSize=10m
* <p>
* jdk8中:
* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
*/
public class OOMTest extends ClassLoader {
public static void main(String[] args) {
int j = 0;
try {
OOMTest test = new OOMTest();
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); // Class对象
j++;
}
} finally {
System.out.println(j);
}
}
}
二 测试
1 JDK 7 版本
设置为:-XX:PermSize=5m -XX:MaxPermSize=5m
1216
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
at com.atguigu.java.OOMTest.main(OOMTest.java:26)
2 JDK 8版本
设置为:-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
3331
Exception in thread "main" java.lang.OutOfMemoryError: Compressed class space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
at com.atguigu.java.OOMTest.main(OOMTest.java:26)
二 如何解决OOM
1 要解决 OOM 异常或 heap space 的异常,一般的手段是首先通过内存映像分析工具(如 Eclipse Memory Analyzer)对 dump 出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)
2 内存泄漏就是有大量的引用指向某些对象,但是这些对象以后不会使用了,但是因为它们还和 GC ROOT 有关联,所以导致以后这些对象也不会被回收,这就是内存泄漏的问题。
3 如果是内存泄漏,可进一步通过工具查看泄漏对象到 GC Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与 GC Roots 相关联并导致垃圾收集器无法自动回收它们的。掌握了泄漏对象的类型信息,以及 GC Roots 引用链的信息,就可以比较准确地定位出泄漏代码的位置。
4 如果不存在内存泄漏,换句话说就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。