`
twypx
  • 浏览: 14894 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Native函数库导致OOM的触发原理

阅读更多

线上应用经常会出现OutOfMemoryError错误,档案引起原因多种多样,包括堆内存溢出(Java Heap Space)、方法区持久代内存溢出(Permanet Space)以及本文需要说道的Native操作本地内存出现的内存溢出问题。要说明这个问题,就先要说明Native函数库是怎样操作内存空间的。

NIO中的Native函数库

在JDK1.4中新加入了NIO类,引入一种基于渠道与缓冲区的I/O方式,它可以通过本机Native函数库直接分配本机内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。所以由Native函数库引起的OOM大多是由于直接使用了本地直接内存(Direct Memory)。

NIO之所以引入该机制,这样能在一些场景中显著提高性能,因为避免了在Java对和本机堆中来回复制数据。显然本机直接内存的分配不会受到Java堆大小的限制,但是即然是内存那肯定还是要受到本机物理内存(包括SWAP区或者Windows虚拟内存)的限制的,一般服务器管理员配置JVM参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError异常。

关于如何定位OOM异常,请参看我的另一篇文章《一次线上OOM排查经过》。

设定JVM参数

按照jvm规范,本地直接内存的最大值按以下顺序设定:

  1. 通过-XX:MaxDirectMemorySize=<size>指定值
  2. 若(1)未满足,则就取maxMemory,也就是通过-Xmx设定的值;
  3. 若(1)、(2)都未满足,则取默认值:64M;

根据以上知识,结合此次测试情况,问题基本水落石出:

假设在我们测试日常机中,系统启动的时候设定-Xmx 3072m,没有通过-XX:MaxDirectMemorySize设定本地直接内存最大值,因此本地直接内存最大值就是-Xmx设定的值3072m,整个系统的物理内存为4G,除掉系统进程占用的内存,剩下的物理内存加上swap空间也就接近3G。设想JVM的heap size占用了1.5G,direct memory使用了1.5G,这时候程序申请一100M的direct内存,在这种情况下无论是JVM heap size还是direct memory不满足触发gc的条件,于是jvm向os申请分配内存,但是OS却无可分配的内存了,于是就会抛出OutOfMemoryError错误。

因此,在使用NIO框架时的时候一定要注意:如果该NIO框架使用的直存,需谨慎设定JVM运行参数,最好用-XX:MaxDirectMemorySize进行设定,否则你就得清楚你设定的-Xmx不单单制定了heap size的最大值,它同时也是direct memory的最大值。

NIO与OOM

一、首先对于可用内存这一概念的理解

在32位机器上,CPU可寻址的物理内存空间最大是4G,超出4G将不再可见。【此处忽略PAE支持,如果进程中使用了AWE(windows)或者mmap(linux)一类的方案,这里暂时不管了】

这4G的物理内存空间又分为用户空间和内核空间。默认情况下,windows按照50:50的比例划分,linux默认下用户空间3G,内核空间1G。

所以一个进程可用的物理内存空间,在linux32位机器下,就是3G。而在64位机器下,基本上可以认为是没有任何限制,原理很简单了。。。

不管是linux还是windows,可用内存空间由:物理内存+swap/虚拟内存组成。Linux上称作swap【交换空间】,windows上称作虚拟内存,本质上都是拿磁盘的一块地方当作物理内存使用。程序是不用关心使用的是物理内存,还是swap;程序操作的是虚拟地址空间, OS再将虚拟地址空间映射到物理内存、文件或者其他。不管是操作物理内存,还是swap,对于程序来说完全是透明的。

Swap/虚拟内存啥时候会使用,这个我也没完全搞清楚,不过有一点应该没错的,就是进程新申请的内存,不会在swap/虚拟内存中分配,而是直接在物理内存中分配。当内存紧张时,OS会将活动进程中占用的内存,从物理内存中交换出来,放到Swap/虚拟内存上(有时甚至内存不紧张也会这么干)。当进程恢复活动时,OS再将数据从swap/虚拟内存空间中读出来放到物理内存中

所以当需要分析和计算进程需要占用的内存空间时,可以简单地忽略swap/虚拟内存的概念【这一点需要深入再论证一下!】

二、JVM对内存的管理

20120929195754_774

JVM内存空间主要分成三部分:

1.  堆空间

包括年轻化、年老代、持久域【以SUN HOTSPOT虚拟机实现为例,其他虚拟机会有区别,比如IBM的虚拟机,所谓的“持久域”不是在堆分配,而是在本地内存】

如果这个空间不够了,会抛出java.lang.OutOfMemoryError

2.  栈空间

每个线程都会有一个单独的stack空间,JDK5.0以前默认好象是256K,JDK5.0默认是1M,很大的一个数值,可以通过-Xss设置。如果这个空间不够了,会抛出java.lang.StackOverflowError

3.  本地内存

Jvm进程可使用的内存,除去堆、栈空间之后,剩下来的就是本地内存

以上三个空间加起来的内存,就是最终jvm进程所使用的所有内存。如果是在32位机器下,不能超过用户空间大小,即3G;在64位机器下,就要看物理内存的大小了

另再提醒一下大家,在发生了内存不足时,一味地增加-Xms和-Xmx,很有可能会适得其反,道理应该很明显了。需要看OOM的类型,是堆不足,还是栈(StackOverFlow)不足,还是本地内存不足native memory。jvm一般都会有足够的信息提示的。

三、Nio的direct memory allocate

我理解的,NIO的直接内存分配【DMA】,应该是从本地内存区域中分配内存。像前面讲的,如果不使用-XX:MaxDirectMemorySize设置,那它就会使用-Xms的设置,以日常测试环境为例, 这种情况下DMA需要3G,堆也需要3G,很明显实际上这两个空间得到的内存都不可能这么大,所以要么是堆空间被挤压,拿不到3G,要么是DMA拿不到足够的空间

看jvm抛出来的错误,应该是堆空间被挤压导致的。如果是本地内存不足,抛出的应该是OutOfMemoryError :Direct buffermemory,可以看一下java.nio.DirectByteBuffer这个类的源码,98行

四、NIO2.0的改进

NIO的DMA,性能肯定比在堆中分配要好得多,因为是直接操作本地内存,避免了数据在JVMHeap和本地内存之间的拷贝操作,尤其是数据量较大时应该更加明显。

 

更多关于JAVA架构知识分享,欢迎访问《架构师之路》。

分享到:
评论

相关推荐

    Groovy大量计算导致oom的解决办法

    问题原因分析:使用ScriptEngine.eval每次都会对脚本进行编译,生成一个新的类,被GroovyClassLoader加载,大量执行计算后,将导致被加载的类数量不断增加,最终OOM。 解决办法:对计算的表达式expression进行预...

    大量加大drawable下图片,导致OOM,使用二次裁剪

    当应用程序加载过多的大尺寸图片时,可能会触发"Out Of Memory"(OOM)错误,导致应用崩溃。这个问题在标题“大量加大drawable下图片,导致OOM,使用二次裁剪”中被提及,暗示了解决这一问题的一种策略——二次裁剪...

    springboot中@Async默认线程池导致OOM问题

    SpringBoot 中 @Async 默认线程池导致 OOM 问题 在 SpringBoot 中使用 @Async 注解来实现异步操作是一种非常常见的做法,但是如果不小心,可能会导致 OOM(Out of Memory)问题。本文将详细介绍 SpringBoot 中 @...

    Keras 快速解决OOM超内存的问题

    在运行几次循环之后,就会报错OOM。 解决方法是在每个代码后面接clear_session()函数,显示的关闭TFGraph,再重启。 详情参考 https://keras.io/backend/#backend-functions。 from keras import backend as K K....

    poi:适合解析小的excel文件,文件稍微大一点就出现OOM。

    事件驱动解析是把文件转换成xml,然后一边读取一边解析,这样就对内存的占用就会很少,可以很好的处理poi出现OOM的问题。 maven添加需要的jar包 &lt;groupId&gt;org.apache.poi &lt;artifactId&gt;poi &lt;version&gt;3.15 ...

    MySQL OOM(内存溢出)的解决思路

    大部分情况下,会杀掉导致OOM的进程,然后系统恢复。通常我们会添加对内存的监控报警,例如:当memory或swap使用超过90%时,触发报警通知,需要及时介入排查。 如果已经出现OOM,则可以通过dmesg命令查看,CentOS7...

    安卓 OOM内存

    安卓 OOM内存

    经典源码图片处理防止OOM Listview Viewpager

    在Android应用开发中,"经典源码图片处理防止OOM Listview Viewpager" 是一个非常重要的主题,特别是对于那些需要大量加载图片...在使用开源库的同时,也要了解其工作原理,以便在必要时进行定制化调整,满足特定需求。

    gif加载动画不oom

    本文将详细介绍如何在Android中实现GIF加载而不触发OOM问题。 首先,我们需要理解为什么GIF加载容易导致OOM。GIF格式的图片包含了连续的帧,当使用默认的Bitmap解码方式时,会一次性加载所有帧到内存中,这样对于大...

    教你分析9种OOM常见原因及解决方案.docx

    OOM 9种常见原因及解决方案 以下是OOM 9种常见原因及解决方案的知识点: 1. Java Heap Space 错误 * 原因分析:请求创建一个超大对象、超出预期的访问量/数据量、过度使用终结器、内存泄漏 * 解决方案:通过 -Xmx...

    解决帧动画OOM问题

    然而,当帧数过多或者图片尺寸过大时,系统可能无法有效管理这些内存,导致OOM。 为了解决SurfaceView中的帧动画OOM问题,有以下几个关键策略: 1. **优化图片资源**:减小图片的大小和分辨率可以显著降低内存占用...

    jvm 老年代oom gc.log

    一次jvm 老年代oom 的dump文件样例

    MySQL Slave 触发 oom-killer解决方法

    MySQL Slave 触发oom-killer的情况通常是因为MySQL进程消耗了过多的内存,导致操作系统认为它引发了内存不足(Out Of Memory)的问题。在这种情况下,Linux内核会启动oom-killer机制来杀死消耗内存最多的进程,以...

    OOM分析工具-MemoryAnalyzer.zip

    当JVM运行时遇到OOM异常,或者通过`jmap`命令手动触发dump,都可以生成这类文件。通过对.hprof文件的解析,MAT提供了多种视图和功能,以帮助我们理解内存分配和使用情况。 1. ** dominator tree 视图**:这是MAT的...

    GridView解决OOM

    GridView通过Adapter来绑定数据源,当Adapter中的每一项包含大图时,如果不进行优化,每次滚动都会加载新的图片到内存,导致内存占用持续增加,直至触发OOM。 **LruCache简介** LruCache(Least Recently Used ...

    安卓内存OOM分析

    如果一个应用持续占用大量内存,系统为了保护整体稳定性和用户体验,可能会触发OOM Killer,选择性地杀死一些内存占用高的进程。 **内存泄露分析:** 内存泄露是导致OOM的常见原因之一。当一个对象不再使用但仍然被...

    android解决OOM

    在Android开发中,"OOM"(Out of Memory)是一个常见的问题,它指的是应用程序在运行过程中耗尽了可用的内存,导致系统无法分配更多的内存资源,从而引发崩溃。为了解决这个问题,开发者需要深入理解Android内存管理...

    lab8_oom实验说明1

    在“lab8_oom”实验中,我们将通过一个内核模块来模拟触发OOM Killer的场景,以便深入理解其工作原理。 实验步骤首先,我们需要编译lab8的内核模块并将其部署到Qemu虚拟机中。运行模块后,观察日志,可以看到一系列...

    Spark面对OOM问题的解决方法及优化总结1

    在Spark运行过程中,内存溢出(OOM)问题可能导致任务失败,影响整个应用程序的效率。本篇文章主要探讨Spark面对OOM问题的解决方法及优化策略。 首先,我们需要了解Spark的内存模型。Spark的Executor内存分为三个...

Global site tag (gtag.js) - Google Analytics