资讯专栏INFORMATION COLUMN

程序内存溢出问题分析

IT那活儿 / 1915人阅读
程序内存溢出问题分析
点击上方“IT那活儿”,关注后了解更多内容,不管IT什么活儿,干就完了!!!





场景来源




户现场有一套重要的业务系统,供业务同事进行使用,但是每连续运行10天都会出现问题,会造成程序慢响应,无响应情况,严重影响消费者、使用者的使用体验,经常得到业务同事反馈,所以配合研发对该问题进行查询。


该系统硬件服务器性能足够,但每个重启周期内内存都是缓慢上升,观察JVM使用率、系统内存使用率等均是持续上升,未发现有主动进行内存回收,初步怀疑为内存泄漏导致。






初识JVM虚拟机



Java语言是一种计算机编程语言,拥有跨平台,面向对象,泛型编程的特性,广泛应用于企业级Web应用的开发和移动应用的开发。
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
Java程序是如何实现跨平台运行的呢?
主要就是通过JVM虚拟机来运行的。





JVM虚拟机的结构




Java虚拟机主要分为五大模块:


  • 类装载器子系统
  • 运行时数据区
  • 执行引擎
  • 本地方法接口
  • 垃圾收集模块
其中垃圾收集模块在Java虚拟机规范中并没有要求Java虚拟机垃圾收集,但是在没有发明无限的内存之前,大多数JVM实现都是有垃圾收集的。而运行时数据区都会以某种形式存在于每一个JAVA虚拟机实例中,但是Java虚拟机规范对它的描述却是相当抽象。
这些运行时数据结构上的细节,大多数都由具体实现的设计者决定。
当我们程序运行后会申请内存空间,其中java栈本地方法栈,程序计算器是JVM虚拟机自动管理的,我们可以管理的区域是堆区和方法区,经常说的java内存调优调的就是这两个区域。




常见的内存溢出




现象一:


java.lang.OutOfMemoryError: Java heap space
该报错为堆溢出,这种场景最为常见主要原因可能有:
  • 代码中可能存在大对象分配。

  • 可能存在内存泄露,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。

解决方法:
  • 检查是否存在大对象的分配,最有可能的是大数组分配。

  • 如果没有找到明显的内存泄露,可以临时 -Xmx 加大堆内存。

  • 还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性。

现象二:

java.lang.OutOfMemoryError: PermGen 
spacejava.lang.OutOfMemoryError: Metaspace
永久代是 HotSot 虚拟机对方法区的具体实现,存放了被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等。
JDK8后,元空间替换了永久代,元空间使用的是本地内存。该报错为永久代/元空间溢出,可能原因有如下几种:
  • 在Java7之前,频繁的错误使用String.intern()方法 。

  • 运行期间生成了大量的代理类,导致方法区被撑爆,无法卸载 。

  • 应用长时间运行,没有重启。

解决方法:
  • 检查是否永久代空间或者元空间设置的过小。

  • 检查代码中是否存在大量的反射操作 。

  • dump之后通过mat检查是否存在大量由于反射生成的代理类。

现象三:

java.lang.OutOfMemoryErrorGC overhead limit exceeded
这是JDK6新加的错误类型,一般都是堆太小导致的。
Sun 官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。
解决方法:
  • 检查项目中是否有大量的死循环或有使用大内存的代码,优化代码。

  • 添加参数 -XX:-UseGCOverheadLimit  禁用这个检查,其实这个参数解决不了内存问题,只是把错误的信息延后,最终出现 java.lang.OutOfMemoryError: Java heap space。

现象四:

java.lang.OutOfMemoryError : unable to create new native Thread


出现这种异常,基本上都是创建的了大量的线程导致的,以前碰到过一次,通过jstack出来一共8000多个线程。

解决方法:
  • 通过 -Xss 降低的每个线程栈大小的容量。

  • 线程总数也受到系统空闲内存和操作系统的限制,检查是否该系统下有此限制:

/proc/sys/kernel/pid_max
    /proc/sys/kernel/thread-max
    maxuserprocess(ulimit -u
    /proc/sys/vm/maxmapcount
现象五:
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
这种情况一般是由于不合理的数组分配请求导致的,在为数组分配内存之前,JVM 会执行一项检查。要分配的数组在该平台是否可以寻址(addressable),如果不能寻址(addressable)就会抛出这个错误。
解决方法:
检查你的代码中是否有创建超大数组的地方。
现象六:
java.lang.OutOfMemoryError: Out of swap space
这种情况一般是操作系统导致的,可能的原因有:
  • swap 分区大小分配不足。

  • 其他进程消耗了所有的内存。

解决方案:
  • 其它服务进程可以选择性的拆分出去 。

  • 加大swap分区大小,或者加大机器内存大小。





紧急处理方法



程序挂掉运维层面可以从系统message日志中查看是否有OOM杀掉进程报错;如果有第一现场,在条件允许的情况下,可以dump内存使用情况,通过工具分析可以更直观的了解内存具体使用到了那些地方。
当然,如果能确认为内存溢出情况,万能重启大法可以先临时解决供业务恢复,等代码修复上线后再从根本上修复问题。



本文作者:臧二飞

本文来源:IT那活儿(上海新炬王翦团队)

​​​

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/129551.html

相关文章

  • JVM问题情景分析

    摘要:问题分析之死锁产生死锁必须同时满足以下四个条件互斥条件一段时间内某资源只能被一个线程进程占有,若有其他请求线程只能等待。问题分析之内存泄露内存溢出堆内存溢出内存泄露指的是申请内存后无法释放该内存。 问题分析之死锁 产生死锁必须同时满足以下四个条件: 互斥条件:一段时间内某资源只能被一个线程(进程)占有,若有其他请求线程只能等待。 不剥夺条件:一个线程占用某资源后只能该线程自己释放资...

    SnaiLiu 评论0 收藏0
  • 关于JVM内存溢出的原因分析及解决方案探讨

    摘要:内存溢出分配的内存空间超过系统内存。内存泄漏的原因分析由大块组成堆,栈,本地方法栈,程序计数器,方法区。内存溢出的原因分析内存溢出是由于没被引用的对象垃圾过多造成没有及时回收,造成的内存溢出。小结栈内存溢出程序所要求的栈深度过大导致。 showImg(https://segmentfault.com/img/bVbweuq?w=563&h=300); 前言:JVM中除了程序计数器,其他...

    xuexiangjys 评论0 收藏0
  • Java内存溢出(OutOfMemoryError)

    摘要:那就只能是处理的数据超过了堆区内存上限,按照这个猜测往下分析。主要暴增对象如上图框出来的地方。符合对象内存一篇文中分析的字节大小。优化自己的程序,使其在运行过程中占用内存尽可能的少。针对异常的具体优化措施。 前言 在正式开始讲解关于OutOfMemoryError错误之前先来了解下,我在遇到这个异常的背景。 对数据充满敬畏之心 我需要对hive中的数据进行批量操作处理,对于没有了解过h...

    calx 评论0 收藏0
  • Java 内存结构备忘录

    摘要:本文详细描述了堆内存模型,垃圾回收算法以及处理内存泄露的最佳方案,并辅之以图表,希望能对理解内存结构有所帮助。该区域也称为内存模型的本地区。在中,内存泄露是指对象已不再使用,但垃圾回收未能将他们视做不使用对象予以回收。 本文详细描述了 Java 堆内存模型,垃圾回收算法以及处理内存泄露的最佳方案,并辅之以图表,希望能对理解 Java 内存结构有所帮助。原文作者 Sumith Puri,...

    wow_worktile 评论0 收藏0

发表评论

0条评论

IT那活儿

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<