一、出现内存溢出的几种情况
内存溢出错误分为StackOverflowError和OutOfMemoryError,前者是栈中出现溢出,后者一般是堆或方法区出现溢出,简称OOM
1. 栈溢出 StackOverflowError
栈溢出一般都是因为没有正确的结束递归导致的,无限递归导致超出栈内存(-Xss)限制时就会抛出StackOverflowError。这种情况直接根据异常信息定位到代码位置进行修正即可。
2. 方法区溢出 OOM
当方法区中加载的类过多,比如通过动态代理生成很多代理类或者热部署时热加载了过多的类,多到超出方法区内存限制时(-XX:MaxMetaspaceSize),会抛出OutOfMemoryError。但这种情况一般也比较少见,如果真出现这种情况可以考虑增加MetaspaceSize,或者拆分服务,使得一个服务使用的类不超出限制。
3. 堆溢出 OOM
其实大部分OOM都是发生在堆区,当堆中存储的对象过多,GC来不及回收或者回收不掉,没有足够空间创建新对象,就会抛出OutOfMemoryError。
java.lang.OutOfMemoryError: Java heap space
二、OOM排查思路
当堆区出现OOM时,就需要我们去进行排查,看什么对象把内存吃满了
- 第一步:我们需要拿到发生OOM时堆区的内存快照heap dump,这里面保存了某一时刻堆中对象的情况。heap dump有两种方式可以拿到。第一种就是我们预先在jvm进程启动时开启参数配置-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath=/usr/local/oom,当发生OOM时会自动生成heap dump保存在指定路径下,这也是推荐的方式。第二种方式是如果jvm进程没有被结束,可以用工具比如jdk自带的jvisualvm手动获取指定进程id的heap dump。
- 第二步:将拿到的heap dump导入到分析工具中,比如说MemoryAnalyzer,通过工具分析可以看到是哪些类型对象占用了大量内存及其GC引用链,还有错误栈等信息。
- 第三步:根据分析得到的信息到代码中进行排查,如果是代码逻辑有问题就改代码,比如不恰当的强引用导致的内存泄漏。如果代码逻辑没问题,确实需要占这么多内存,那就考虑提升堆内存大小(-Xms -Xmx)。
参考:
https://blog.csdn.net/qq_31363843/article/details/117038001
https://blog.csdn.net/weixin_41010294/article/details/104009722