方法区溢出
在Java内存区域的Java虚拟机栈
章节介绍过,方法区主要存储类型相关信息
,在JDK 6及以前版本字符串常量池
也在此区域存储,JDK 7时将字符串常量池移入堆内存。我们可以使字符串常量池
、类型相关信息
导致内存溢出。
字符串常量池
在JDK 6及以前版本字符串常量池
存储在方法区,JDK 7时将字符串常量池移入堆内存。 所以此方法只能在jdk6生效。
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int strCount = 0;
while (true) {
list.add(String.valueOf(strCount++).intern());
}
}
类型相关信息
可以通过产生大量类来填满方法区,使其内存溢出。产生大量类有有多种方式,例如JDK提供的动态代理,第三方的CGLib。
public static void main(String[] args) {
while(true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(obj,objects);
}
});
enhancer.create();
}
}
static class OOMObject{
}
这段代码是一个典型的Java内存溢出(Out Of Memory)示例,它使用CGLib库创建无限个代理对象,从而导致内存不断增长,最终耗尽系统资源。
具体的代码逻辑如下:
-
在
main
方法中,使用一个无限循环while(true)
来不断执行以下步骤。 -
创建Enhancer对象,Enhancer是CGLib库中的一个类,用于生成代理对象。
-
设置被代理类的父类为OOMObject类:
enhancer.setSuperclass(OOMObject.class)
。 -
关闭代理对象的缓存功能,确保每次都会生成新的代理对象:
enhancer.setUseCache(false)
。 -
设置回调函数,即拦截器。在这个示例中,使用了MethodInterceptor接口,并实现了其中的intercept方法。该方法会在代理对象的方法被调用时触发,通过调用methodProxy.invokeSuper方法实现对原始方法的调用。
-
调用
enhancer.create()
方法创建代理对象,并丢弃返回值。
以上步骤将会重复执行,不断创建代理对象。由于没有终止条件,内存中会不断创建新的代理对象,逐渐耗尽系统资源,最终导致内存溢出。