【高级Java工程师】线上问题之内存爆炸 分析、定位与解决

前言

内存爆炸、OOM,CPU过高,线上程序反应太慢等问题,作为一个程序员这些问题每个人都有耳闻,但是细想一下又离我们很遥远,很多人好几年可能都没遇到过一次。
线上问题不容易碰到,但是碰到了不能解决就来不及了,就算没碰到自己不会解决心理也会很慌,所以一起学习吧

一、 内存配置说明

1.1 生产环境内存一般是多大?

内存一般是 6-16GB,内存过高对于JVM垃圾回收机制也会有不同,所以内存不是越大越好,当然处理大数据时内存有可能会配置比较高

1.2. JVM内存占用

我们都知道java服务器的内存大小设置参数:-Xmx 和 -Xms ,但是服务器启动时,并不会立马去申请最大内存数的内存,而是内存不够是才会去申请。通过添加参数: -XX:+AlwaysPreTouch 让服务器启动的时候就会去申请最大内存,减少再去申请内存时造成的消耗(小优化,作用不大)

二、 常见内存问题

2.1. 频繁调用GC

无论哪种GC策略,肯定都是要占用系统资源的(Stop the World),影响用户程序的执行,频繁调用gc这个问题应该在测试阶段就要被发现。采用server模式运行,加上打印GC日志参数:-XX:PrintGCDetails -Xloggc:gc.log,,添加该参数后gc日志会打印在在一个gc.log的文件中,我们就可以看到gc被调用的情况,很容易就能知道gc调用是否过多。
【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第1张图片

2.1.1 gc调用太频繁一般也有两种原因
  • 程序本身内存占用很大,造成内存总是不足,jvm自己频繁调用gc
    【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第2张图片
    • 应用程序调用了gc,一般我们写的程序并不会去调用gc,但是引用的第三方包有时候会调用,比如jxl的导入excel之后默认会掉一次System.gc() 【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第3张图片
      问题来了,虽然能从日志上面看到gc 被频繁调用,但是怎么定位到底是哪个第三方包呢?
      使用Btrace工具,这个工具有两种打开方式:命令行和集成化工具jvisualvm
      jdk安装目录下都有jvisualvm工具,下面是使用方法:
    1. 安装Btrace workbench插件,也就是把btrace 功能集成到jvisualvm这个工具中【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第4张图片
    2. 安装好这个插件后我们就可以在左边看到我们自己正在运行的应用程序,右键应用程序可以为该程序写脚本
      【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第5张图片
      【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第6张图片
      从这个脚本的运行结果来看,就一眼就看出来了是哪个应用程序的哪个类调用了gc。该工具的原理就是字节码的动态修改,动态代理也是动态修改字节码
    2.2. OOM
    OOM很难预测,因为要是能提前知道肯定早就去解决了,OOM出现一般程序也挂了,gc日志啥的也没参考性,只能重启,但是重启后又完全重现不了问题,很烦心。想想,要是出现OOM时能够留下日志,指示出哪里造成的OOM不是很美滋滋吗?
    - 留下证据,加上参数:-XX:+HeapDumpOnOutOfMemoryError 生产环境的标配,这个参数的作用就是OOM时会生成堆内存快照,以文件的形式保存起来,文件名称格式java_pid线程号.hprof。有了这个文件之后就是对这个文件进行分析了,用Memory Analyzer工具【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第7张图片
    也会预测哪里会出现问题
    【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第8张图片
    也可以按包进行统计占用内存的大小
    【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第9张图片
2.3. 堆外内存

堆外内存一般用的比较少,但是也会用到,比如:NIO的ByteBuffer.allocateDirect(1024102416);这个方法就是在堆外存上面申请16M的内存,所以堆外内存也存在释放的问题,另外生产环境一定要设置堆外内存大小,比如:-XX:MaxDirectMemorySize=128m
堆外内存溢出的主要解决思路就是在测试环境重现问题
2.3.1 用Btrace工具来跟踪堆外内存的申请

下载工具包到服务器
【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第10张图片
步骤:
1. 大概估计那些地方使用的申请堆外内存的方法
2. 编写脚本,监视这些申请堆外内存的方法的调用情况
【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第11张图片
3. 上传脚本到服务器上面,用btrace命令执行脚本
【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第12张图片
通过这个工具我们就知道哪些地方调用了预测的申请堆外内存的方法
2.3.2 替换JVM的内存申请工具包,用Google提供的gperftools
这个工具包能够打印每次内存申请的日志,使用方法:
在这里插入图片描述

  1. 用gperfgools 替换jvm默认的内存申请工具,然后运行自己写的程序,会打出starting tracking the heap ,说明已经在记录内存申请日志了,随着程序的运行,会生成相应的内存申请日志在这里插入图片描述
  2. 分析日志
    在这里插入图片描述 把分析结果重定向到一个report.txt 文件中,方便我们自己查看
    【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第13张图片
    gperftools的原理
    本来jvm的内存申请步骤是这样的
    【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第14张图片
    用了gperftools后
    【高级Java工程师】线上问题之内存爆炸 分析、定位与解决_第15张图片
    就是替换了jvm自己提供的申请内存工具包

小结

本文主要讨论是生产环境中内存常见的问题及解决方案,围绕着gc过于频繁、OOM、堆外内存溢出,提供多种方法排查问题。

你可能感兴趣的:(高级java工程师,java,内存,内存爆炸,线上问题,分析与解决)