您的位置:首页 > 编程语言 > Java开发

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

2018-02-01 14:08 736 查看

背景

最近在深入研究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为何引入元空间的概念 ,我讲讲我的看法

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

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

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


至此已经找到了问题原因。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐