问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

内存溢出(OOM)的场景分析及解决方案

创作时间:
作者:
@小白创作中心

内存溢出(OOM)的场景分析及解决方案

引用
CSDN
1.
https://m.blog.csdn.net/m0_68921558/article/details/144913887

内存溢出(Out Of Memory,简称OOM)是指应用系统中存在无法回收的内存或使用的内存过多,最终使得可用内存无法保障程序运行所需。此时程序就运行不了,系统会提示内存溢出,一般会导致系统重启。

要了解内存溢出的原因,我们需要先了解一些JAVA应用程序运行的基本知识:

  1. 任何一个JAVA程序(比如:xxx-service),总是需要在JAVA虚拟机(JVM)中运行。JVM会开辟三种类型的内存空间供JAVA程序去使用:
  • 元空间:主要是程序加载时使用,用于加载程序中的类和常量等;
  • 堆空间:程序运行时,当有对象被引用时使用,比如new一个对象时,对象的方法和属性等;
  • 方法区:程序运行时,用于对象的值、属性的值、计算时的中间数据等;
    注:JVM还会开辟一些内存空间用于自身机制的需要,这里不作探讨。
  1. 任何一台JVM,总是要在某台服务器上运行。采用容器化部署时,先将JVM部署在容器中,容器再运行于服务器上。这种服务器称之为POD。一个POD上可以有多个容器。多个容器共享这个POD的资源;

  2. POD就是容器化部署的最小单元。我们使用K8S来管理多个POD及其容器,叫做集群化管理。我们会给每个POD设置一个最大使用内存(一般是2G-4G)。很显然,JVM的运行不能超过其POD的内存,而java程序的运行则不能超过JVM自身设置的最大内存。

基于以上的认识,内存溢出分为三种场景:

  1. 元空间内存溢出,此种堆栈异常信息为:java.lang.OutOfMemoryError: Metaspace;(注:jdk8以下的版本是将“Metaspace”换成"PermGen space")。一般是Java应用程序在加载类和常量等基础数据时,元空间内存不足导致,此种情况发生的概率较小,一般是元空间设置不合理或Java应用程序设计不合理,解决方案为:
  • 调整元空间内存;
  • 将java应用程序拆解成多个。
  • 如果在程序运行时出现了该异常,则重点关注是否使用了动态编译和反射技术,因为它们使用的也是元空间内存;
  1. 堆内存溢出:此种堆栈异常信息为:java.lang.OutOfMemoryError: Java heap space;(注:jdk8以下的版本是将“Java heap space”换成"PermGen space",可见jdk8对内存溢出有了更细的区分,以便于开发者定位问题)。理论上,堆内存也属于元空间的一部分(所以jdk8以前的版本,其异常信息是一样的),区别在于堆内存是会被java的垃圾回收机制进行回收的,而其它元空间被使用后会一直固定占用,这就是为什么程序一旦启动,无论有没有被访问,总是持续稳定地占用了一部分内存资源而不会被回收走的原因。
    此种情况发生的原因一般是:堆内存设置不合理或短时间内创建了大量对象来不及回收;解决方案为:
  • 调整堆内存;
  • 检查程序中是否存在循环或递归中创建对象的情况;
  1. 方法区内存溢出:此种堆栈异常信息为:java.lang.OutOfMemoryError: GC overhead limit exceeded;这是最常见的内存溢出场景;一般都与程序设计、代码性能等多种因素息息相关;

无论是何种场景的内存溢出,K8S的调度机制都是通过自动重启POD的方式来释放内存;同时,会在钉钉群“生产云监控-容器集群告警”中进行提示:

想要具体查看重启前的内存使用情况,则需使用K8S的POD管理工具:这里要重点关注内存爬升的是陡坡还是斜坡

当然,最重要的还是分析内存溢出时的堆栈内容:

这里需要注意的地方是:此时的堆栈信息,表示的是程序正在执行这个进程时,内存溢出了;大部分的情况都是这个进程的问题导致的内存溢出,但也有可能该进程只是"压倒骆驼的最后一根稻草",此时需结合pingpoint工具来分析当时是否存在多并发的其它慢请求,共同将内存占用推向了溢出临界点。

这里总结了一些我们以前踩过的坑,供大家参考借鉴:

  • 程序进入无限循环或递归。
  • 超大数据查询。通常发生在导出/导入、XXLjob调度、下拉框控件、多表关联查询等,同时相应的后端接口又未做返回行数的限制。
  • 内存泄漏的场景,指产生了大量无法有效回收的内存数据,使得内存占用一直居高不下,少量的其它进程便可轻易触发OOM事件。如:
  • LIST、MAP等集合类对象使用后没有进行clear操作;
  • 变量使用不规范:使用一个变量时越界或定义了变量却从未使用,滥用全局变量和静态变量,等等。
  • 界面查询一个较大的数据,中途异常退出,也可能导致回收失效。

如果此时想分析泄漏的内存数据,可使用MemoryAnalyzer工具。

OOM事件带来的影响:

  1. 最直接的影响是用户体验:操作时常中断、系统响应越来越慢、系统稳定性差;
  2. 容易造成数据丢失:因为重启时该POD上的请求会被异常中断;
  3. 在程序幂等性未完善的情况下,造成数据重复,因为用户的操作被中断后,又重复操作;
  4. 高频的OOM事件,可能引发服务器层面更严重问题;

所以,关于内存的正常合理的使用,我们需要经常关注K8S的POD管理工具,一般内存的使用率在30%-50%较为合理,如果长期使用在70%以上,就要考虑是否内存泄漏或增加内存的方式减少OOM事件的频发。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号