Java内存溢出错误(OutOfMemoryError)总结

由于java虚拟机提供了垃圾内存回收机制GC,这令使用java的开放者明显比较少地遇到内存溢出的错误,所以一直以来没有系统地总结java的内存溢出的问题,最近遇到一些内存溢出错误,趁这个机会进行总结,包括一直陌生的JVM内存参数的设置。

要明白java内存溢出错误的情况,首先要明白java内存的划分,见之前的文章《Java内存布局和类加载的一些总结》,简单来说就是分三个大块:堆内存,栈内存(包括本地栈),方法区(包括本地方法区),那么内存的溢出通常来说也是按照上面的划分分成堆内存溢出,栈内存溢出,方法区内存溢出。

最常见的就是堆内存溢出:

我在使用BitSet实现布隆过滤器时,由于需要的BitSet对象太大,在32位windows系统上运行时,直接报出了java.lang.OutOfMemoryError: Java heap space,当然,如果需要测试该抱错,可以在eclipse上设置堆内存的大小参数:-Xms20M -Xmx20M -Xmn10M,这样堆的最大内存就被限定在20M了,创建对象超过20M就会报上面的错误。

栈内存溢出:

一般来说,栈内存溢出是比较少见的,原因基本上分两个,一是方法调用太深,尤其是递归方法的深度调用,另个原因是创建线程太多,即使每个线程占有的栈内存不大,但是乘上大量的线程数就会占用内存比较大。调用过深导致栈内存不足的报错:java.lang.StackOverflowError,明显可以看到栈stack的字眼,如果是因为创建线程过多导致栈内存不足报错:OutOfMenoryError:unable to create new native thread,信息是无法创建线程,栈内存参数的手工设置参数-XSs10M。

方法区内存溢出:

这种溢出错误就更少见了,由于方法区主要存储的是类的信息和静态数据,包括String,静态变量,方法代码。所以一般能导致方法区内存溢出的原因要么是动态创建和加载的类太多了,要么是String对象太多了。报错日志:OutOfMemoryError:PermGen space 永久代内存溢出。不过现在jdk8.0已经没有了永久代的概念而用元数据区的概念取代,可能会爆出Meta space元数据区溢出的信息,元数据区内存大小的配置参数:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M

 

以上是正常的三种内存溢出的情况,可以根据报错信息来分辨和判断是哪个内存区出现了内存溢出,然后再根据这个了推断具体原因。堆内存溢出无非就是对象创建太多而且无法及时回收或者堆内存配置过少,栈内存溢出分方法调用太深,一般由于递归方法没控制好,或者是线程分配太多了。方法区(元数据区)内存溢出要么是动态创建的类太多了,要么是String对象太多了。

 

除了这三种情况,其实还有一种因为java使用的文件映射机制导致主机内存使用率太高造成整个主机的内存不足而出现主机挂死的情况。简单来说就是java为了加快文件读取的效率绕过了操作系统内核,直接把文件映射到了java进程的虚拟内存空间,所谓虚拟内存是和实际物理内存相对应的,说白了就是根据主机的位数能分配的理论上的最大空间,32位主机虚拟内存为2的32次方,64位的主机虚拟内存为2的64次方,而实际物理内存没这么大,从概念上每个java进程都占用着主机的虚拟内存空间,实际使用的物理内存空间却比这个小很多,通过映射机制使用实际的物理内存。如果使用的内存操作了实际物理内存能提供的最大容量就由主机从磁盘划出的交换区来补充,是补充而不一定是补足,因为如果主机是64位的话,虚拟内存空间高达16777216T,主机也不可能划得出这么大的磁盘来补足,而这部分交换区和内存的数据交换操作也称为缺页中断,或者称为swap操作。频繁进行缺页交换操作的话,主机性能会大幅下滑甚至挂死。而所谓的文件映射机制就是直接把要读取的文件映射到虚拟内存中,当java程序读取该文件时就产生缺页中断,通过类似swap交换的方式交换到物理内存中,说白了就是把文件当成了补充的swap区,这样程序就绕过了操作系统内核,直接和文件交互,加快了文件操作的效率,但是这种情况下,如果使用不当就会大量消耗主机内存最后造成内存使用过大,出现严重的频繁中断交换操作,最终导致整个主机挂死。好像目前的cassandra最新版本采用了内存映射机制,可能会造成上述情况。所以当在前面三种正常情况不可能成立的时候,程序本身又使用了文件映射机制时出现主机内存占有过大导致主机性能过低的故障时,可以往这方面去考虑。这种内存又称为java程序的DirectMemory,可以通过jvm的内存参数配置进行限制:-XX:MaxDirectMemorySize=10M

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值