Exception in thread "Thread-0" java.lang.OutOfMemoryError: GC overhead limit exceeded 解决方法

18 篇文章 0 订阅
18 篇文章 0 订阅

背景

最近在深入研究jvm底层机制,写了一个测试类一直加载class,模拟加载类过多导致Perm永久代报:java.lang.OutOfMemoryError:PermGen space 内存溢出。

测试代码

package com.xyq.maventest.visualvm;

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class TestPermGen {


    private static List<Object> insList = new ArrayList<Object>();

    public static void main(String[] args) throws Exception {
        permLeak();
    }

    private static void permLeak() throws Exception {
        for (int i = 0; i < 10000; i++) {
            URL[] urls = getURLS();
            URLClassLoader urlClassloader = new URLClassLoader(urls, null);
            Class<?> logfClass = Class.forName("org.apache.commons.logging.LogFactory", true,urlClassloader);
            Method getLog = logfClass.getMethod("getLog", String.class);
            Object result = getLog.invoke(logfClass, "TestPermGen");
            insList.add(result);
            System.out.println(i + ": " + result);
        }
    }

    private static URL[] getURLS() throws MalformedURLException {
        File libDir = new File("C:/Users/youqiang.xiong/.m2/repository/commons-logging/commons-logging/1.1.1");
        File[] subFiles = libDir.listFiles();
        int count = subFiles.length;
        URL[] urls = new URL[count];
        for (int i = 0; i < count; i++) {
            urls[i] = subFiles[i].toURI().toURL();
        }
        return urls;
    }

}

代码很简单,就是一直加载log日志jar下的类,直到发生永久带溢出

测试

配置Run Configurations 的jvm启动参数: -Xms128M -Xmx128M -XX:PermSize=128m -XX:MaxPermSize=256m

永久代初始化内存:128m
永久代最大内存:256m
这里写图片描述

然后点击run 运行,此时后台一直打印类的记录
这里写图片描述

进入cmd命令,输入jvisualvm 打开 jvisualvm 图形界面,观察内存的变化趋势,此时会发现内存会一直飙涨。
这里写图片描述

正常情况下,很快系统会抛出java.lang.OutOfMemoryError:PermGen space 异常

因为永久代最大内存:256m ,循环加载类的过程中256m 很快就会被撑爆。
但是很奇怪的事情是,我等了将近几分钟,系统在控制台打印出
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded 异常信息,
这里写图片描述

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.xyq.maventest.visualvm.TestPermGen.permLeak(TestPermGen.java:26)
    at com.xyq.maventest.visualvm.TestPermGen.main(TestPermGen.java:17)
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.zip.InflaterInputStream.<init>(InflaterInputStream.java:88)
    at java.util.zip.ZipFile$ZipFileInflaterInputStream.<init>(ZipFile.java:393)
    at java.util.zip.ZipFile.getInputStream(ZipFile.java:374)
    at java.util.jar.JarFile.getInputStream(JarFile.java:447)
    at sun.misc.URLClassPath$JarLoader$2.getInputStream(URLClassPath.java:977)
    at sun.misc.Resource.cachedInputStream(Resource.java:77)
    at sun.misc.Resource.getByteBuffer(Resource.java:160)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:454)

跟我最初假设的情况不一致。这是什么问题呢?

初步定为

开始猜想是不是因为JVM参数配置问题?
-Xms128M -Xmx128M -XX:PermSize=128m -XX:MaxPermSize=256m 几经思考,不应该,这几个参数就是jvm的配置,后来多次修改参数的大小。重新执行依然报同样的错误。

后面冷静想了想,仔细分析打印的日志异常。

Exception in thread "RMI TCP Connection(idle)" Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0

这句话的意思是,PermSize=128m 这个参数 不被支持在jdk1.8 。

oh my god!

后来查询资料才知, jdk1.8 中已经摒弃了 Perm的配置,引入了一个新的概念Metspace。至于jdk1.8为何引入元空间的概念 ,我讲讲我的看法

  1. Perm持久代分配内存大小不好估算,分配内存太大容易导致永久代内存溢出,太小容易导致持久代内存溢出

  2. 如果一个应用中有过大的static属性、方法的话,可能会导致系统无法启动

  3. 1.8之前类的信息在jvm分配中,而在1.8引入的metspace概念并不属于jvm内存一部分,原则上内存的打消跟系统内存有关。

解决步骤

知道了原因后,那就好办了,重新修改jvm配置参数-Xms128M -Xmx128M -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m,启动TestPermGen 。这次没过过久果然打印出了想要的结果:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.xyq.maventest.visualvm.TestPermGen.permLeak(TestPermGen.java:26)
    at com.xyq.maventest.visualvm.TestPermGen.main(TestPermGen.java:17)
Caused by: java.lang.OutOfMemoryError: Metaspace
    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
    at java.lang.Class.getConstructor0(Class.java:3075)
    at java.lang.Class.newInstance(Class.java:412)
    at org.apache.commons.logging.LogFactory.createFactory(LogFactory.java:1160)
    at org.apache.commons.logging.LogFactory$2.run(LogFactory.java:1065)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:1062)
    at org.apache.commons.logging.LogFactory.getFactory(LogFactory.java:650)
    at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:704)
    ... 6 more

至此已经找到了问题原因。

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值