jvm 内存溢出 - 方法区及运行时常量池溢出
Posted 稚枭天卓
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm 内存溢出 - 方法区及运行时常量池溢出相关的知识,希望对你有一定的参考价值。
Java 永久代是非堆内存的组成部分,用来存放类名、访问修饰符、常量池、字段描述、方法描述等,因运行时常量池是方法区的一部分,所以这里也包含运行时常量池。我们可以通过 jvm 参数 -XX:PermSize=10M -XX:MaxPermSize=10M
来指定该区域的内存大小,-XX:PermSize
默认为物理内存的 1/64 ,-XX:MaxPermSize
默认为物理内存的 1/4 。String.intern()
方法是一个 Native 方法,它的作用是:如果字符串常量池中已经包含一个等于此 String 对象的字符串,则返回代表池中这个字符串的 String 对象;否则,将此 String 对象包含的字符串添加到常量池中,并且返回此 String 对象的引用。在 JDK 1.6 及之前的版本中,由于常量池分配在永久代内,我们可以通过 -XX:PermSize
和 -XX:MaxPermSize
限制方法区大小,从而间接限制其中常量池的容量,通过运行 java -XX:PermSize=8M -XX:MaxPermSize=8M RuntimeConstantPoolOom
下面的代码我们可以模仿一个运行时常量池内存溢出的情况:
import java.util.ArrayList;
import java.util.List;
public class RuntimeConstantPoolOom
public static void main(String[] args)
List<String> list = new ArrayList<String>();
int i = 0;
while (true)
list.add(String.valueOf(i++).intern());
运行结果如下
[root@9683817ada51 oom]# ../jdk1.6.0_45/bin/java -XX:PermSize=8m -XX:MaxPermSize=8m RuntimeConstantPoolOom
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at RuntimeConstantPoolOom.main(RuntimeConstantPoolOom.java:9)
还有一种情况就是我们可以通过不停的加载class来模拟方法区内存溢出,《深入理解java虚拟机》中借助 CGLIB 这类字节码技术模拟了这个异常,我们这里使用不同的 classloader 来实现(同一个类在不同的 classloader 中是不同的),代码如下
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
public class MethodAreaOom
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException
Set<Class<?>> classes = new HashSet<Class<?>>();
URL url = new File("").toURI().toURL();
URL[] urls = new URL[]url;
while (true)
ClassLoader loader = new URLClassLoader(urls);
Class<?> loadClass = loader.loadClass(Object.class.getName());
classes.add(loadClass);
运行结果如下:
[root@9683817ada51 oom]# ../jdk1.6.0_45/bin/java -XX:PermSize=2m -XX:MaxPermSize=2m MethodAreaOom
Error occurred during initialization of VM
java.lang.OutOfMemoryError: PermGen space
at sun.net.www.ParseUtil.<clinit>(ParseUtil.java:31)
at sun.misc.Launcher.getFileURL(Launcher.java:476)
at sun.misc.Launcher$ExtClassLoader.getExtURLs(Launcher.java:187)
at sun.misc.Launcher$ExtClassLoader.<init>(Launcher.java:158)
at sun.misc.Launcher$ExtClassLoader$1.run(Launcher.java:142)
at java.security.AccessController.doPrivileged(Native Method)
at sun.misc.Launcher$ExtClassLoader.getExtClassLoader(Launcher.java:135)
at sun.misc.Launcher.<init>(Launcher.java:55)
at sun.misc.Launcher.<clinit>(Launcher.java:43)
at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1337)
at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1319)
在 jdk1.8 上运行上面的代码将不会出现异常,因为 jdk1.8 已结去掉了永久代,当然 -XX:PermSize=2m -XX:MaxPermSize=2m
也将被忽略,如下
[root@9683817ada51 oom]# java -XX:PermSize=2m -XX:MaxPermSize=2m MethodAreaOom
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=2m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=2m; support was removed in 8.0
jdk1.8 使用元空间( Metaspace )替代了永久代( PermSize ),因此我们可以在 1.8 中指定 Metaspace 的大小模拟上述两种情况
[root@9683817ada51 oom]# java -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m RuntimeConstantPoolOom
Error occurred during initialization of VM
java.lang.OutOfMemoryError: Metaspace
<<no stack trace available>>
[root@9683817ada51 oom]# java -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m MethodAreaOom
Error occurred during initialization of VM
java.lang.OutOfMemoryError: Metaspace
<<no stack trace available>>
说明
文章摘自《深入理解Java虚拟机》第二版 周志明著,仅作为学习记录,书籍中用到的案例代码及描述有部分修改,但未改变原意。
以上是关于jvm 内存溢出 - 方法区及运行时常量池溢出的主要内容,如果未能解决你的问题,请参考以下文章