线上JVM问题,排查(持续更新)

尽管JVM已经自动帮你做了一些管理内存的工作,但是这不代表了JVM就不会出现问题,真实的情况总是各种各样,根据不同的环境,各种各样的问题暴漏出来,也不断的可以体现出 当前编写代码的能力吧。

 出现问题,查找原因,解决问题,能力提升。一步一步来吧。


java.lang.OutOfMemoryError 

这个东西 估计每个JVM程序员都见过的。产生这个异常的原因大致上有3个方面;

  1. java.lang.OutOfMemoryError: Heap Size或GC overhead limit exceeded
  2. java.lang.OutOfMemoryError: Unable to create new native thread
  3. java.lang.OutOfMemoryError: PermGen Space


java.lang.OutOfMemoryError: Heap Size

这个异常的意思就是 对象占用的空间 已经超出 JVM中堆空间的大小,如果经常的 非常频繁的出现 这个信息,一般来说 除非你JVM中故意给堆空间设置了很小的空间,二般情况下不用考虑了,程序写的有问题,创建的对象回收不了,好好检查代码去吧,看看哪里的引用释放不掉。


java.lang.OutOfMemoryError: GC overhead limit exceeded

出现这个异常 的情况 是 JVM这个异常的情况是,JVM花了大量的时间在GC,JVM去GC本来是很正常的事,但是花大量的时间,这个大量的时间如果来定义呢,Sun官方对此的定义是:“并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作“。
并不是所有的应用都要加这个配置的


  1. 比如oschina的blog分站,没什么特别重要的内容要处理的,就可以使用-XX:-UseGCOverheadLimit这个参数关闭掉这个检查
  2. 又比如说,一些 支付,交易,和钱有关的网站,可能某个业务使用了一个JVM本地的Queue,当这个检查 检测到当前JVM要挂,就可以把这个Queue里的数据 紧急的使用 MappedByteBuffer的force强制把(重要的业务队列)数据写入到存储设备,这样这些数据就不会丢,还可以接着处理,避免给用户带来不好的体验、影响。


出现了上面俩个问题 怎么解决才是头等大事,问题总要一步一步解决的

1 . :如果当前程序是 发布到测试环境的,可以给jvm添加如下2个参数,以便下次又出现这个错误了,可以有点数据依据 去分析 -XX:+HeapDumpOnOutOfMemoryError -XX:HeadDumpPath=yourpath

2 .  : 如果JVM还存活,可以到处dump 就 用jmap -dump , 用MAT 分析 导出的内容 ,这样方式只是可以告诉你 当前堆空间中 存在了大量 哪些对象,通常 帮助很小。如果是一些byte[] String char boolean 这样的东西,一下也找不出个头绪来,也别急你还可以找一位大神,他叫 btrace(精通解决各种JVM疑难杂症),这一部分在别的文章里面 说明。


java.lang.OutOfMemoryError: PermGen Space

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类的对象实例的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理。

解决办法:设置 -XX:PermSize=128M 就可以了,128M可以存放很多类信息了,不够再调.


java.lang.OutOfMemoryError: Unable to create new native thread

出现了这个异常,首先执行2个命令(Linux):

# ps -eLf | grep java -c
280
# ulimit -u
30475  



如果第1命令的结果已经达到2个命名的结果,就增大 ulimit 的值,一般也不建议增太大,有问题还是要暴漏出来的。 这种情况下常见的有可能是用了 Executors.newCachedThreadPool 这种来创建了一个没限制大小的线程池。有时候你使用JDK自带的这线程池 也会有 坑,要多留意。

还有一种情况(来自网上)是:

内存也空闲,但仍然创建不了,这有可能是由于在2.6.18/32内核上kernel.pid_max默认的32768造成的,这个值其实直接限制了最多能创建的线程数就是32768(即使ulimit -u的值比这大也没用)


还有一种OOMnative OOM,就是物理内存被耗光,对于这种现象,解决起来会麻烦一些,从经验上来说,Native OOM有很大概率是由于错误使用Deflater/Inflater造成的,所以在碰到这类现象时,可以先用btrace跟进下看看使用了Deflater/Inflater的有没有显式去调用end方法;另外一种常见的原因是使用Direct ByteBuffer的场景(例如NIO框架等),如使用了Direct ByteBuffer的对象是比较长存活的,当其被转到旧生代后,在fgc没触发前,其实其占用的JVM堆外内存是不会被释放的,在这种情况下,可以做的一个尝试是先强制执行几次fgc(jmap -histo:live),然后看看堆外内存的使用是不是下降了,如果下降了则说明是这个问题,对于这类问题,可以用的一个解决方案是增加一个启动参数:-XX:MaxDirectMemorySize=500m来实现当Direct ByteBuffer使用到500m后主动触发fgc来回收(到底设置成多大应用可以自己调整)



 

jmap -heap <pid>

jstat -gcutil <pid>  10000 20 ,对比下看是不是有大量回收动作,是的话没准有内存泄露了

jmap -histo <pid>





参考:

http://bluedavy.me


你可能感兴趣的:(线上JVM问题,排查(持续更新))