详解java JVM方法区Java.Lang.OutOfMemoryError

466 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

方法区异常:Java.Lang.OutOfMemoryError: Metaspace

方法区,用于存放对象的元信息,比如class相关信息,如类名、访问修饰符、常量池、字段描述、方法描述、静态变量、JIT编译后的代码等,运行时产生大量的类,会填满方法区,造成溢出。Java8之后用元空间Metaspace 取代了PermGen永久代 ,元空间使用的是本地内存。jdk后把放在永久带的字符串常量池移除了。

java.lang.OutOfMemoryError: PermGen space   before jdk 1.8
java.lang.OutOfMemoryError: Metaspace    after jdk 1.7

原因分析:

  • 使用CGLib生成了大量的代理类,导致方法区被撑爆
  • 在Java7之前,频繁的错误使用String.intern方法
  • 应用长时间运行,没有重启JVM
  • 加载了重复的类

解决思路:

  • 检查元空间/永久代是否设置的过小 :-XX:MaxPermSize / -XX:MaxMetaspaceSize=512M
  • 检查代码中是否存在大量的动态代理反射操作
  • 检查是否使用CGLib生成了大量的代理类
  • 重启JVM

案例分析

maven引入javassist依赖, javassist是一个高性能字节码工具。

 Javassist 使 Java 字节码操作变得简单。它是一个用于在 Java 中编辑字节码的类库。它使 Java 程序可以在运行时定义新类,并在 JVM 加载它时修改类文件。与其他类似的字节码编辑器不同,Javassist 提供了两个级别的 API:源级别和字节代码级别。如果用户使用源代码级 API,则他们可以在不了解 Java 字节码规范的情况下编辑类文件。整个 API 仅使用 Java 语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码。Javassist 可以即时对其进行编译。另一方面,字节码级别的 API 允许用户像其他编辑器一样直接编辑类文件

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.23.1-GA</version>
</dependency>
虚拟机参数设置:-Xms200m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:MaxMetaspaceSize=15m

public class Meta {

    static javassist.ClassPool cp = javassist.ClassPool.getDefault();
    public static void main(String[] args) throws CannotCompileException {
        int i =0;
        while (true){
          Class c = cp.makeClass("eu.my.demo.Generated"+i++).toClass();
        }
    }
}

报错

java.lang.OutOfMemoryError: Metaspace
Dumping heap to java_pid89312.hprof ...
Heap dump file created [50293482 bytes in 0.283 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
        at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:249)
        at javassist.ClassPool.toClass(ClassPool.java:1120)
        at javassist.ClassPool.toClass(ClassPool.java:1083)
        at javassist.ClassPool.toClass(ClassPool.java:1041)
        at javassist.CtClass.toClass(CtClass.java:1278)
        at org.jvm.Meta.main(Meta.java:16)

分析过程

使用MAT打开上面堆转存快照java_pid89312.hprof如下图:

总共26.9MB堆空间,

问题比较明显不停的创建class造成元空间不足,存在内存泄漏需要定位到相关代码进行修改。

推荐工具

JVM 内存分析工具 mat

1、Eclipse Memory Analyzer

www.eclipse.org/mat

阿里云 APM 产品,支持 OOM 异常关键字告警

2、ARMS

help.aliyun.com/document_de…

阿里 Java 在线诊断工具 Arthas(阿尔萨斯)

3、alibaba Arthas

github.com/alibaba/art…

参考文档

blog.csdn.net/allenjsl/ar…

segmentfault.com/a/119000002…

Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation

cloud.tencent.com/developer/a…

juejin.cn/post/690866…