OutOfMemory (OOM)的类型与检测

Java堆溢出

Java堆存的是对象的实例,所以无限添加对象实例很容易造成堆溢出

public class OutOfMemoryError_Heap {
    static class OOMObject{}
    public static void main(String[] args) {
        List <OOMObject> list = new ArrayList<OOMObject>();
        while(true) {
            list.add(new OOMObject());
        }
    }
}

添加虚拟机参数
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/jj/Desktop
a)Xms20m 堆最小值
b)Xmx20m 堆最大值
c)HeapDumpOnOutOfMemoryError 内存堆转储快照
d)XX:HeapDumpPath=/Users/jj/Desktop 快照存放地址

结果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to /Users/jj/Desktop/java_pid584.hprof ...
Heap dump file created [27568513 bytes in 0.137 secs

使用MAT内存分析工具分析生成的快照可以看到
这里写图片描述
Shallow Heap是指对象本身占有的内存大小。
Retained Heap是指对象本身加上它引用的对象的大小。
这里OOMObject 的Shadow Heap和Retained Head相同的原因是OOMObject没有引用;而他是16的原因是对象头在64位机器上占有64bit,也就是16个字节,但是由于虚拟机自动使用压缩指针,所以占有12个字节,但是又由于对象占有的内存必须为8的倍数,所以最后结果是16字节。
字节长度计算的方式是 12+类型长度和->最接近8倍数的数字->结果

虚拟机栈和本地方法溢出

栈内存太小和已使用的栈空间太多都会导致虚拟机栈或本地方法溢出。

a)栈溢出

public class JavaVMSStack {
    private int stackLength =1 ;

    public void JavaVMSStack() {
        stackLength ++ ;
        JavaVMSStack();
    }
    public static void main(String[] args) {
        JavaVMSStack j = new JavaVMSStack();
        try {
            j.JavaVMSStack();
        } catch (Exception e) {
            System.out.println(j.stackLength);
            throw e;
        }
    }
}

虚拟机参数
-Xss160k

这里写图片描述

b)内存溢出

我们知道,虚拟机会为每个线程的栈先分配一段可以调整的内存,每个线程分配到的栈帧越大,可以建立的线程就越少,建立线程时就越容易把剩下的内存耗尽。


public class JavaVMSStackOOM {
    private void dontStop() {
        while (true) {
        }
    }

    public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    dontStop();
                }
            });
            thread.start();
        }

    }


    public static void main(String[] args) {
        JavaVMSStackOOM oom = new JavaVMSStackOOM();
        oom.stackLeakByThread();
    }
}

虚拟机参数:

-vss20M

在Windows平台的虚拟机中,Java的线程是映射到虚拟机的内核上的,隐私上述代码执行时有较大的风险,可能会导致系统假死。

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

运行时常量池是方法区的一部分。
在jdk1.6以前,由于常量池位于永久代中,而我们可以通过参数去限制方法区的大小,所以间接影响到常量池的容量。
而jdk1.7以后, 已经把原本放在永久代的字符串常量池移出, 放在Java堆中(元空间Metaspace)中,元数据并不在虚拟机中,使用的是本地的内存。

可以通过以下参数来指定元空间的大小:
  
  -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

 -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值