常见的内存溢出的原因及其解决思路
1.堆溢出:
由于大量的对象都直接分配在堆上,因此它最有可能发生溢出。因为大量对象占据了堆空间,而这些对象都持有强引用,导致无法回收,当对象大小之和大于堆空间时就会发生溢出。
为了解决堆溢出错误,一方面可以使用-Xmx参数指定一个更大的堆空间,另一方面,由于堆空间不可能无限增长,通过MAT或者Visual VM等工具,分析找到大量占用堆空间的对象,并在应用程序上作出合理的优化也是十分必要的。
2.直接内存溢出
在Java的NIO(New IO)中,直接内存的使用,也就是通过Java代码,获得一块堆外的内存空间,这块空间是直接向操作系统申请的。直接内存的申请速度一般要比堆内存慢,但是其访问速度要快于堆内存。因此对于那些可复用的,并且会被经常访问的空间,使用直接内存可以提高系统性能。但是由于直接内存没有被Java虚拟机完全托管,若使用不当,也会发生内存溢出而宕机。
3.过多线程导致OOM
由于每一个线程的开启都要占用系统内存,因此当线程数量太多时,也有可能导致OOM。由于线程的栈空间也是在堆外分配的,因此和直接内存非常相似,如果想让系统支持更多的线程,应该使用一个较小的堆空间。
4. 永久区溢出
永久区(Perm)是存放类元数据的区域。如果一个系统定了太多的类型,那么永久区是有可能溢出的。在JDK1.8中,永久区被一块成为元数据的区域替代,但是他们的功能是类似的,都是为了保存类的元信息。
一般来说,要解决永久区溢出的问题,可以从以下几个方面考虑;
- 增加MaxPermSize的值
- 减少系统需要的类的数量
- 使用ClassLoader合理的装载各个类,并定期进行回收。
5.GC效率低下引起的OOM
GC是内存回收的关键,如果GC效率低下,那么系统的性能会受到严重的影响。如果系统的堆空间太小,那么GC所占的时间就会较多,并且回收所释放的内存就会较少。根据GC占用的系统时间,以及释放的内存的大小,虚拟机会评估GC的效率,一旦虚拟机任伟GC的效率过低,就有可能抛出OOM异常: GC overhead limit exceeded.