在jdk1.6及以前的版本中,常量池放在Perm区也便是方法区中,因此在jdk1.6版本中,常量池溢出能够说是方法区溢出。html
示例一:java
方法区溢出的示例见《JVM体系结构之七:持久代、元空间(Metaspace) Metaspace 之三--了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap》网络
示例二:jvm
在网络上看到一个cglib致使的方法区溢出的示例:借助CGLib使方法区出现内存溢出异常(在对类进行加强时,都会使用到CGLib这类字节码技术,加强的类越多,就须要越大的方法区来保证动态生成的Class能够加载入内存。另外,JVM上的动态语言(例如Groovy等)一般都会持续建立类来实现语言的动态性,随着这类语言的流行,也愈来愈容易遇到与下面代码类似的溢出场景)post
pom.xml中增长cglib:url
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.6</version> </dependency>
package com.dxz.jvm; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @VM args : -XX:PermSize=10M -XX:MaxPermSize=10M */ public class MethodAreaOutOfMemory { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MethodAreaOutOfMemory.TestObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { return arg3.invokeSuper(arg0, arg2); } }); enhancer.create(); } } public static class TestObject { private double a = 34.53; private Integer b = 9999999; } }
运行结果:spa
方法区溢出也是一种常见的内存溢出异常,一个类要被垃圾收集器回收掉,断定条件是比较苛刻的。在常常动态生成大量Class的应用中,须要特别注意类的回收情况。这类场景除了上面提到的程序使用了CGLib字节码加强和动态语言以外,常见的还有:大量JSP或动态产生JSP文件的应用(JSP第一次运行时须要编译为Java类)、基于OSGi的应用(即便是同一个类文件,被不一样的加载器加载也会视为不一样的类)等。code
在jdk1.7之后,perm持久代已经被移除,常量池也放到了堆中,其致使的溢出也变成了堆溢出。xml
示例见《JVM体系结构之七:持久代、元空间(Metaspace) Metaspace 之三--了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap》htm
见《JVM体系结构之三:方法区之2(jdk1.6,jdk1.7,jdk1.8下的方法区变迁)》
上面示例2在jdk8中运行,因为方法区被移除了,那么经过反射生成的一系列对象存放到哪儿了?可看到jvisualVM中的元空间不断增加。