系统堆栈、内存、线程异常预防及排查

异常预防及排查

异常预防及排查

以下命令请勿轻易在生产环境尝试,如需日常调试需屏蔽负载

异常预防

预防为主,排查为辅,提前给服务器配置合理的JVM参数,加强系统监控及管控,减少问题

  • 1)堆(Heap)和非堆(Non-heap)内存
    按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。
    可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,
    所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
  • 堆内存分配
    JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
    空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。
    说明:如果-Xmx 不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,无法用try…catch捕捉。
  • 非堆内存分配
    JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。(还有一说:MaxPermSize缺省值和-server -client选项相关,
    -server选项下默认MaxPermSize为64m,-client选项下默认MaxPermSize为32m。这个我没有实验。)
    上面错误信息中的PermGen space的全称是Permanent Generation space,是指内存的永久保存区域。还没有弄明白PermGen space是属于非堆内存,还是就是非堆内存,但至少是属于了。
    XX:MaxPermSize设置过小会导致java.lang.OutOfMemoryError: PermGen space 就是内存益出。
    说说为什么会内存益出:
    (1)这一部分内存用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同。
    (2)GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS 的话,就很可能出现PermGen space错误。
    这种错误常见在web服务器对JSP进行pre compile的时候。

  • 参数详解

    参数字典:https://www.cnblogs.com/cheng21553516/p/11221114.html
    推荐加的参数有:

    1. -server:该参数提供服务模式启动
    2. -XX:+PrintGCDetails:该参数禁止程序主动FullGC,防止代码调用导致的FullGC

    TRP服务器参数推荐(系统虚拟机32C64G)
    -Dfile.encoding=UTF-8 -server -Xms32768m -Xmx32768m -Xmn16384m -XX:MetaspaceSize=16384M -XX:SurvivorRatio=10 -XX:MaxTenuringThreshold=15 -XX:NewRatio=2 -XX:+DisableExplicitGC -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xlo
    ggc:/data/apache-tomcat-8.0.47/logs/gc.$$.log

系统排查
  • 查看系统当前简况
    jcmd 查看当前服务器有哪些java程序

    jstat -gc pid 当前程序GC情况

    以下两个参数,可以反映出当前系统是否存在过多的FullGC,可多次运行对比:

    1. FGCFullGC次数
    2. FGCTFullGC总停止时间
  • 保存现场,回复系统
    根据刚才的PID,可以使用jmap -heap pid 进行简单的堆内存查看

    当判断系统内存使用率过高,且不下降时,简单的排查无法定位问题,则必须保留线程,导出dump文件,然后恢复系统(如重启),导出的命令如下

    1. jmap -dump:format=b,file=2020.**.hprof -F pid
    2. ps:如果系统内存已满,命令无法执行,需要加 -F,注:该命令对系统的内存消耗较大,执行时需要时刻监控应用状态

    导出的文件下载到本地pc,进行分析,分析工具推荐eclipse MAT
    免安装版本eclipse MAT,下载地址:http://www.eclipse.org/mat/downloads.php
    PS:由于对PC配置要求较高,需下载64位

修改配置文件

改成符合文件大小的内存

打开mat,导入前面导出的dump文件,使用Dominator Tree和Leak Suspects进行分析

Leak Suspects会直接排可能有问题的点

总结:很多内存问题,都是由于对内存的预估不足,导致内存溢出,其实内存泄漏的情况比较少,内存溢出一般在遵循开发规范后大部分可以避免的。
线程问题
  • 简单查看线程情况
    top -H -p pid \找出java进程中cup使用率较高的线程

printf ‘%x\n’ pid 由于线程地址为16进制,故需转换成16进制

然后可以找到CPU占用率最高线程的执行详情
jstack PID|grep nid -A 30 \找到对应线程id的堆栈信息

当前GC线程最活跃

  • 线程信息分析
    打印当前所有线程信息
    jstack -l 16808 >jstack.log
    打印完成后,查看当前系统线程各个状态数量
    cat jstack.log | grep “java.lang.Thread.State” | sort -nr | uniq -c

    主要查看当前系统是否存在Block状态线程

  • 分析线程导出信息文件
    使用vim编辑器打开前面导出的线程信息

搜索对应的代码开头,统计相对的相同的线程数量,隔断时间多次对比,如果线程长时间存在处于待运行RUNNABLE状态,则需要排查该段代码

posted @ 2020-10-27 14:43  DaviLin  阅读(391)  评论(0)    收藏  举报