Java OOM系列(三)

来源:互联网 发布:弟子知错了请师傅责罚 编辑:程序博客网 时间:2024/05/16 08:45

这里写图片描述

概述

上篇讲到了OOM时,如何使用工具查看java内存的内存结构中都存储了多少内存,从而定位出是哪块内存发生了泄漏,以求针对性的解决。通常OOM会伴随着错误提示,不过很多情况下的错误提示并不能帮我们找到原因,比如用户启动了很多线程,导致在启动数据库连接时失败,这种情形下报的错误会是数据库连接失败,而不是线程数过多。当然,有些情况下,根据提示还是可以找到问题的。关于OOM的第三篇文章,主要介绍一些可能经常会遇到的引发OOM的原因,在这种原因下如何排查问题,以及可能的解决方案。

内存溢出

  1. 堆内存溢出基本会报两种错误:GC overhead limit exceeded/Java Heap Space,当jvm gc行为中超过98%以上的时间去释放小于2%的堆空间时会报这个错误,意味着Java Heap分配不出必要的内存了。这种情形,需要查看具体的代码。
    • 首先,确认机器物理内存使用状态,机器内存不至于太小;
    • 其次,jinfo -flags 确认一下,确认不是heap size太小的问题。通常-Xms(初始堆大小)和-Xmx(最大堆大小)在生产环节会设置成完全一样,原因是如果不设置成一样,比如-Xms小于-Xmx,造成的结果是java内存到达一定大小后,觉得内存不够用了而又没到最大值,会做一遍GC把内存放大;当觉得要缩小,也会先做GC再缩小。最后因为这两个参数设置不一样,造成了多次无用GC。所以线上生产环节不允许这两个值不同。
    • 这种问题的解决思路可以是:先拿到heap dump文件(建议gcore,然后在用jmap从core文件中提取出java内存dump文件),然后用MAT或者jhat分析dump文件,大致找到有疑问的类,然后使用btrace跟踪代码,一般就能定位问题。
    • 这里拿到dump文件以后,分析的话,一般dump文件有多大,就需要电脑有多大的内存,否则解析dump文件的时候也会报OOM。。。
    • 通常,引用未释放会引起堆内存的OOM。
    • 使用自增长的数据结构,比如Map,比如List。有些人喜欢把数据已Map的形式存内存,当时可能数据1W条,10W条,不会OOM,但是指不定哪天数据突然上到1000W条,那就OOM了,1000W并发量的梦想总是要有的。。。
    • 还有可能,是不是代码里面有死循环,怀疑死循环的话,可以使用jstack查看一下线程栈的状态;
    • GC频繁要注意还有可能是由于缺少一个参数: -XX:+UseCMSInitiatingOccupancyOnly
  2. 第二种错误是 unable to create new native Thread,出现这个错误的原因大概有三种
    • 线程创建太多,超过了线程栈大小限制,创建不出线程了,可以使用jmap -heap 或者 jstat 看一下栈内存使用状况,也可以jstack查看线程栈的详细状态。通常要求线程是要起名字的,这样jstack命令能非常清晰的区分我们定义的每一个线程。还可以使用btrace跟踪new Thread 和 ThreadPoolExecutor
    • 线程数超过了ulimit限制。linux会给每个用户做限制,比如每个用户最多创建2000个线程,如果超过了2000就会报这个错。ulimit -a 可以看到所有的limit。生产环境,建议把线程数的ulimit设置为unlimited。ps -eLf | grep java -c命令可以统计java进程创建了多少线程
      这里写图片描述
    • 内核层面,线程数超过了kernel.pid_max。
      这里写图片描述
      系统中使用到多线程的地方,一定要使用线程池限制线程数目无限制增长
  3. 永久代溢出:PermGen Space。classLoader每装载一个类,都会把这个类的所有信息写进PermGen,包括类的名字方法的名字方法体等等。类装载越多PermGen占用越多。PermGen用满后,在java8之前会触发full GC。
    • 永久代溢出的原因通常是装载类太多了,一般使用btrace跟踪谁调了ClassLoader.defineClass方法,就能看到谁一直在加载类信息,就能定位到问题。
    • 定位到问题以后,确认一下是否必须要加载这么多类,如果是的话,调大PermSize
  4. Direct Buffer memory。写通讯类程序的人看这个问题多一点,Netty等背后在做网卡字节流装载是用Direct ByteBuffer。
    • 默认, Direct Buffer大小和Java堆大小一致。就有可能,由于网络抖动或者其他网卡字节流问题,Direct Buffer占满整个java堆,造成溢出;
    • 使用btrace跟踪ByteBuffer.allocateDirect,一般能跟踪到产生问题的根源代码
    • 为避免这种问题,可以设置Direct buffer最大值:-XX:MaxDirectMemorySize
  5. 其他还有一些情况会造成OOM,比如 Map failed, 或者request {} bytes for {}. Out of swap space?,最有效解决办法是先google,毕竟博主对这样的问题还没有经验,找到可以引起问题的原因以后,使用btrace跟踪代码,一般可以找到原因。

——————-我是微信公众号分割线—————-
这里写图片描述

0 0
原创粉丝点击