JVM有哪些运行时数据区
JVM运行时数据区有程序计数器、本地方法栈+虚拟机栈、堆、元数据区、直接内存。
其中只有程序计数器不是内存溢出,其他的都有可能会产生内存溢出。
栈内存溢出
当方法的调用深度过深,可能会导致栈内存溢出。
一般是发生在递归调用的场景中,没有定义好递归基导致的栈内存溢出。
创建线程内存溢出
当内存不够,创建线程也会导致内存溢出。因为每个新的线程都需要占用一定的内存。
- 解决办法
查看操作系统是否有使用线程数的限制。不要随意创建线程,使用线程池来对线程进行管理,创建新的线程池时,也进行仔细的评估,是否有创建线程池的必要。
堆内存溢出
堆中加载了大量的对象,或者有超大对象,或者有很多内存泄露,就会产生堆内存溢出。
当有超大对象时,可以采用分批处理的方式解决,减少单次处理的对象的大小。
有些开发会在数据库中查询大量的数据,然后在内存中进行过滤,这种情况可以通过在查询时,通过sql进行提前过滤,避免查询大量的数据。
内存泄露,就需要避免内存泄露,比如,避免使用过多的全局变量,尽量使用局部变量,如果非要使用全局变量,就需要在使用后及时清理。在使用ThreadLocal时,里面的值如果不使用了,及时调用ThreadLocal的remove方法,避免内存泄露。
直接内存溢出
直接内存一般是用来做文件IO或者网络IO的内存,是直接向操作系统申请内存,大小限制只跟操作系统剩余内存有关,但是也可以指定直接内存大小,当指定了直接内存大小,而程序又申请了非常大的直接内存空间,没有及时清理时,就会发生直接内存溢出。
在使用ByteBuffer中的allocateDirect()的时候会用到,很多javaNIO(像netty)的框架中被封装为其他的方法,出现该问题时会抛出java.lang.OutOfMemoryError: Direct buffer memory异常。
如果你在直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题。
元空间内存溢出
元空间默认大小只跟操作系统的内存大小有关,是直接向操作系统申请内存,默认没有限制,但是最好配置一下,避免整个操作系统都收到影响。
元空间存储的是类字节码文件相关的信息。当系统代码本身很多,或者引用的第三方包很多,或者通过动态代码生成了很多动态的类的字节码,就会导致元空间溢出。
所以需要我们慎用第三方包,移除不需要的第三方包,对于生成大量生成动态类的框架,做好压力测试。
垃圾回收超时内存溢出
当垃圾回收时间超过一定的比例时,就会触发这个异常,这个比例可以通过JVM启动参数进行调节。(具体的参数可以查看poe记录)
- 解决办法
减少对象的生命周期,避免内存泄露。
系统杀死进程内存溢出
在描述该问题之前,先熟悉一点操作系统的知识:操作系统是建立在进程的概念之上,这些进程在内核中作业,其中有一个非常特殊的进程,称为“内存杀手(Out of memory killer)”。当内核检测到系统内存不足时,OOM killer被激活,检查当前谁占用内存最多然后将该进程杀掉。
一般Out of memory:Kill process or sacrifice child错会在当可用虚拟虚拟内存(包括交换空间)消耗到让整个操作系统面临风险时,会被触发。在这种情况下,OOM Killer会选择“流氓进程”并杀死它。
虽然增加交换空间的方式可以缓解Java heap space异常,还是建议最好的方案就是升级系统内存,让java应用有足够的内存可用,就不会出现这种问题。