死磕JVM(三)内存溢出

目录

内存泄漏

内存溢出

1 java堆溢出

1.1 溢出场景

1.2 解决方法

2 虚拟机栈和本地方法栈溢出

2.1 溢出场景

2.2 解决方法:

3 方法区和运行常量池溢出

3.1 溢出场景

3.2 解决方法


死磕JVM(一)内存区域 https://blog.csdn.net/u012133048/article/details/85344025

死磕JVM(二)内存模型 https://blog.csdn.net/u012133048/article/details/87886352

死磕JVM(三)内存溢出 https://blog.csdn.net/u012133048/article/details/87891398

死磕JVM(四) 垃圾回收机制 https://blog.csdn.net/u012133048/article/details/85413539

死磕JVM(五)对象的创建 https://blog.csdn.net/u012133048/article/details/87938452

死磕JVM(六) 类加载机制 https://blog.csdn.net/u012133048/article/details/85378148

死磕JVM (七) 锁优化 https://blog.csdn.net/u012133048/article/details/85490843

死磕JVM (八) 总结 https://blog.csdn.net/u012133048/article/details/88069289

内存泄漏

内存溢出

1 java堆溢出

1.1 溢出场景

java heap space

java堆用于存储对象实例,因此只要不断的创建对象实例,并且保证GC Roots到对象之间有可达性路径来避免垃圾回收机制清楚这些对象,那么在数据量达到最大堆的容量限制后就会产生内存溢出:

比如在运行时,将java堆内存设置会不可扩展,即-Xms与-Xmx设置成一样大小就可以避免自动扩展,通过参数-XX:HeapDumpOnOutOfMemoryError可以在虚拟机在内存溢出时,Dump出当前内存堆的转储快照以便事后分析。

public class HeapOOM{

    static class OOMObject{

    }

    public static void main(){

        List<OOMObject> list = new ArrayList<OOMObject>();
        while(true){
            list.add(new OOMObject);
        }
    }

}

1.2 解决方法

1、设置堆参数-Xms和-Xmx,与物理机内存对比看是否可以跳大

2、通过工具查看是否有内存泄漏引起的内存溢出,排查内存泄漏的代码。

3、查看代码是否有某些对象生命周期过长,持有状态时间过长,尝试减少程序运行期间的内存消耗。

4、减少强引用,增加弱引用和软引用,强引用不会被内存回收。

2 虚拟机栈和本地方法栈溢出

2.1 溢出场景

通过-Xss来设置栈内存容量

在Java虚拟机规范中描述了两种异常:

1、如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出stackOverflowError异常。

2、如果虚拟机在扩展栈时,无法申请到足够的内存,将抛出OutOfMemoryError异常。

这两种异常是比较雷同的,因为第一种异常表示使用的栈空间过大,第二种异常表示栈申请到的内存太小,或者无法申请到内存,说到底都是同个意思,内存空间供不应求。所以在实际情况中,单纯的通过单线程的栈帧操作是获取不到OutOfMemoryError异常的,只会得到stackOverflowError异常。

如果是在多线程下,通过不断建立线程的方式会产生内存溢出,但是这和栈空间就没有关系了,因为每个线程会去独立申请内存,当线程过多无法申请到内存时,就会发生OutOfMemoryError异常。在这种情况下,每个线程的栈分配的内存越大,越会出现内存溢出。

其实这很好理解,因为线程获取到的内存空间 = 总内存 - 最大堆内存 - 最大方法区内存 - 程序计数器,因此每个线程的分配的栈容量越大,能建立的线程数越少。

2.2 解决方法:

1、如果是因为建立过多线程所导致内存溢出抛出OutOfMemoryError异常

   a) 减少线程数量,如果允许通过线程池来代替过多的线程。

   b) 减少最大堆大小和最少栈容量来换取更多的线程。

2、如果是因为建立过多线程所导致内存溢出抛出stackOverflowError异常

   a) 通过-Xss 增大栈内存容量

   b) 优化代码减少本地变量,或者减少递归深度。

3 方法区和运行常量池溢出

PermGen space

3.1 溢出场景

方法区、永久代、以及常量池的关系

方法区是一个抽象概念,永久代是hotSpot对方法区的具体实现(JDK1.7以前)。运行常量池是方法区的一部分,也就是永久代的一部分。不同JDK版本之间有着实现上的差别。

 在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区;

在JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代

在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace) 

运行时常量池溢出常见的情况依我个人理解有两种:

1、1.6以前可以使用String.intern()来实现溢出场景,在1.7用同样的方法只会造成内存溢出。

https://www.cnblogs.com/wangshen31/p/10404353.html

2、在解析阶段在运行时常量池将类常量区加载到运行时常量区,如果此时的运行时常量区的内存大小不足以加载类常量信息,则会抛出OOM。

 

方法区类信息的溢出:

方法区中存储着类信息,如类名,访问修饰符,常量池,字段描述,方法描述等,所以方法区会发生内存溢出的条件就是类信息过多过大,充满整个方法区。

常规类加载一般不会导致方法区溢出,因此方法区溢出一般发生在使用反射,动态代理的场景。

spring的框架中,经常会用CGlib字节码技术,使用CGlib来代理,达到增加类功能的目的,增强的类越多,就需要越大的方法区来保证动态生成的类可以加载到方法区中。因此在使用JVM动态语言持续创建类的场景会遇到方法区内存溢出的异常。

public class MethodAreaOOMTest {

    public static void main(String[] args) {
        int i=0;
        try {
            while(true){
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMObject.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                        return proxy.invokeSuper(obj, args);
                    }
                });
                enhancer.create();
                i++;
            }
        } finally{
            System.out.println("运行次数:"+i);
        }
    }

    static class OOMObject{

    }
}
 

3.2 解决方法

1、通过PermSize和MaxPermSize设置方法区大小,同时也可同步设置常量区。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值