目录
实战-方法区溢出
- 线程共享,用来存储被虚拟机加载的类型信息、常量、静态变量等
方法区的组成
- 大于等于JDK8
- 堆
- 静态变量
- 字符串常量池
- 元空间 (前身叫:持久代)
- 类信息
- 类的版本
- 字段描述信息
- 方法描述信息
- 接口和父类等描述信息
- class文件常量池(静态常量池)
- 运行时常量池
- 类信息
- 堆
示例代码-1
/**
* JDK 6: -XX:PermSize=6m -XX:MaxPermSize=6m
* 报永久代溢出(java.lang.OutOfMemoryError: PermGen space)
* ==========
* JDK 7: -XX:PermSize=6m -XX:MaxPermSize=6m
* 不报错,原因:JDK 7把字符串常量池放到堆了,设置-Xmx6m会报堆内存溢出
* ==========
* JDK 8+:同JDK 7
*/
public class MethodAreaOOMTest1 {
public static void main(String[] args) {
// 使用Set保持着常量池引用
Set<String> set = new HashSet<String>();
int i = 0;
while (true) {
// intern():native方法
// 如果字符串常量池里面已经包含了等于字符串x的字符串;那么
// 就返回常量池中这个字符串的引用
// 如果常量池中不存在,那么就会把当前字符串添加到常量池,
// 并返回这个字符串的引用
set.add(String.valueOf(i++).intern());
}
}
}
示例代码-2
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MethodAreaOOMTest2 {
/**
* Cglib之Enhancer创建动态代理:https://blog.csdn.net/yaomingyang/article/details/82762697
*
* @param args 参数
*/
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Enhanced hello");
// 调用Hello.say()
return proxy.invokeSuper(obj, args);
}
});
Hello enhancedOOMObject = (Hello) enhancer.create();
enhancedOOMObject.say();
System.out.println(enhancedOOMObject.getClass().getName());
}
}
}
class Hello {
public void say() {
System.out.println("Hello Student");
}
}
通过配置元空间的默认值和最大值
VM options: -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
运行Main时候就会抛出:java.lang.OutOfMemoryError: Metaspace 异常。
因为类的定义是放在元空间,而现在超出元空间的最大值。
方法区总结
- 不同版本的JDK,方法区存放的结构不同,相同的代码报错也可能不同
方法区溢出的场景
- 常量池里的对象太大
- 加载的类的 “种类” 太多
- 动态代理的操作库生成了大量的动态类
- JPS项目
- 脚本语言动态类加载
避免方法区溢出
- 根据JDK版本,为常量池保留足够空间
- JDK6: 配置较大的 PermSize、MaxPermSize
- 大于等于JDK7: 配置较大的 Xms、Xmx
- 防止类加载过多导致的溢出
- 小于等于JDK7:配置较大 PermSize、MaxPermSize
- 大于等于JDK8:留空元空间相关的配置,或者设置合理大小的元空间
元空间是本地内存,本地内存足够就基本不会出现溢出
属性 | 作用 | 默认值 |
---|---|---|
-XX:MetaspaceSize | 元空间的初始值,元空间占用达到该值就会触发垃圾收集,进行类型卸载,同事,收集器会自动调整该值。如果能够释放空间,就会自动降低该值;如果释放空间很少,那么在不超过-XX:MaxMetaspaceSize的情况下,可适当提高该值。 | 21810376字节 |
-XX:MaxMetaspaceSize | 元空间最大值 | 受限于本地内存大小 |
-XX:MinMetaspaceFreeRatio | 垃圾收集后,计算当前元空间的空闲百分比,如果小于该值就增加元空间的大小 | 40% |
-XX:MaxMetaspaceFreeRatio | 垃圾收集后,计算当前元空间的空闲百分比,如果大于该值就增加元空间的大小 | 70% |
-XX:MinMetaspaceExpansion | 元空间增长时的最小幅度 | 340784字节 |
-XX:MaxMetaspaceExpansion | 元空间增长时的最大幅度 | 5452592字节 |