方法区溢出

方法去用于存放Class的相关信息,如:类名,访问修饰符,常量池,字符描述,方法描述等。对于这个区域的测试,基本思路是运行时产生大量的类去填满方法区,知道溢出。虽然直接使用Java SE API也可以动态产生类(如反射时的GeneratedConstructorAccessor和动态代理等),但在本次试验使用CGLIB直接操作字节码运行时,生成大量的动态类。

值得注意的是,当前主流的很多框架 如:Spring,Hibernate对类进行增强时,都会使用到类似CGLIB这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载如内存。例如:

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 JavaMethodAreaOOM {
	public static void main(String[] args) {
		while (true) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(OOM.class);
			enhancer.setUseCache(false);
			enhancer.setCallback(new MethodInterceptor() {

				@Override
				public Object intercept(Object obj, Method arg1, Object[] args, MethodProxy proxy) throws Throwable {
					return proxy.invokeSuper(obj, args);
				}
			});
			OOM oom = (OOM) enhancer.create();
			oom.sayHello("Kevin LUAN");
		}
	}

	static class OOM {
		public String sayHello(String str) {
			return "HI " + str;
		}
	}
}

Caused by: java.lang.OutOfMemoryError:PermGen space


JDK 1.7 64位运行结果如下:

java version "1.7.0_45"

Java(TM) SE Runtime Environment (build 1.7.0_45-b18)

Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)


Exception in thread "main" 

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"



方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾回收器回收掉,判定条件非常苛刻,在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景除了上面提到的程序使用GCLIB字节码增强外,常见的还用JSP或动态产生JSP文件的应用(JSP第一次运行时需要编译为JAVA类),基于OSGI的应用(即使是同一个类文件,被不同的加载器加载也会视为不同的类)等。

#增加JVM 参数 来快速定位下 Class load ,来定位下。

-XX:+TraceClassLoading -XX:+TraceClassUnloading

输出格式:[Loaded sun.rmi.server.LoaderHandler from /usr/local/java/jdk1.7.0/jre/lib/rt.jar]可以方便定位出增加的CLASS 文件来源

<think>好的,我现在需要帮用户找到导致Java方法区溢出的示例代码。根据用户提供的引用内容,特别是引用[2]提到的,可以通过生成大量类来填满方法区,比如使用CGLib动态生成类。 首先,我得确认方法区溢出的原因。方法区主要存储类信息、常量池等。如果不断生成新的类,就会占用方法区内存,导致溢出。常见的做法是使用动态代理或者字节码生成库,比如CGLib,来持续创建类。 接下来,我需要编写一个示例代码。记得在Java 8之前,方法区是PermGen的一部分,而Java 8及之后是Metaspace,所以可能需要调整JVM参数。例如,设置MaxMetaspaceSize来限制元空间大小,或者对于旧版本设置PermSize和MaxPermSize。 然后,考虑如何动态生成类。CGLib的Enhancer可以创建代理类。在循环中不断生成这些类,直到方法区被填满。同时,为了避免Full GC回收类,需要保留这些类的引用,比如将它们加入一个静态列表中。 代码结构大致是:创建一个静态列表,循环中使用Enhancer生成代理类,并将它们添加到列表中。运行时需要添加JVM参数,如-XX:MaxMetaspaceSize=10m来限制元空间大小,这样更容易触发溢出。 还要注意异常处理,当方法区溢出时,可能会抛出OutOfMemoryError。另外,用户可能需要根据不同的Java版本调整参数,比如Java 7使用-XX:PermSize和-XX:MaxPermSize,而Java 8及以上使用Metaspace相关参数。 最后,验证这个代码是否有效。运行时会看到Metaspace的OOM错误,说明方法区溢出了。同时,确保代码中没有其他内存泄漏,比如堆内存的问题,避免混淆错误来源。 总结步骤:使用CGLib生成大量类,限制方法区大小,保持类引用防止回收,运行代码触发溢出。这样就能得到用户所需的方法区溢出示例。</think>### Java方法区溢出示例代码及说明 方法区溢出通常由**类元数据过量加载**引起,可通过动态生成大量类来模拟。以下是基于CGLib库的典型实现: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import java.util.ArrayList; import java.util.List; public class MethodAreaOOM { static class OOMObject {} public static void main(String[] args) { List<Object> classHolder = new ArrayList<>(); while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); // 禁用缓存以强制生成新类 enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> proxy.invokeSuper(obj, args1)); classHolder.add(enhancer.create()); // 保留类引用防止被回收 } } } ``` #### 运行参数设置 - **Java 8+**(元空间模式): ```bash -XX:MaxMetaspaceSize=10m -XX:+HeapDumpOnOutOfMemoryError ``` - **Java 7及之前**(永久代模式): ```bash -XX:PermSize=5M -XX:MaxPermSize=5M ``` #### 原理说明 1. **动态类生成**:通过CGLib的`Enhancer`在运行时持续创建`OOMObject`的子类[^2] 2. **元数据累积**:每个生成的类会占用方法区(元空间/永久代)内存 3. **内存保留**:`classHolder`保持类引用,避免被垃圾回收 #### 典型错误输出 ```java java.lang.OutOfMemoryError: Metaspace at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348) at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值