测试能力提升 性能测试之 JVM 异常说明和分析工具

大道 · 2020年06月14日 · 1006 次阅读

StackOverflowError 和 OutOfMemoryError 是 JVM 里的两种 Error。每个运行时区域——程序计数器
、Java 虚拟机栈、本地方法栈、Java 堆、方法区、直接内存发生 Error 的原因和错误信息是不同的。

不是所有的 StackOverflowError 和 OutOfMemoryError 都需要调整参数,要做到正确分析、合理调整

常见异常说明

Java 堆溢出

  1. 关键错误信息
java.lang.OutOfMemoryError:java heap space 
  1. 排查思路

    检查 Java 虛拟机的堆参数设置,与机器的内存对比,看看是否还有向上调整的空间。再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行期的内存消耗。

  2. 参数配置

-Xmx 最大堆大小 默认物理内存的1/64
-Xms 初始堆大小 默认物理内存的1/4(<1GB) 

栈溢出

HotSpot 虚拟机栈=虚拟机栈 + 本地方法栈

  1. 错误信息
java.lang.StackOverflowError
java.lang.OutOfMemoryError
  1. 排查思路
    出现 StackOverflowError 异常时,会有明确错误堆栈可供分析,相对而言比较容易定位到问题所在。
    如果是建立过多线程导致的 OutOfMemoryError,在不能减少线程数量或者更换 64 位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程,通过 “减少内存” 的手段来解决内存溢出。
  2. 参数配置
-Xss 每个线程堆栈的大小。一般情况下256K是足够了。影响此进程中并发线程数大小等。

方法区溢出

  1. 错误信息
JDK1.7以前的错误信息
java.lang.OutOfMemoryError: PermGen space
  1. 排查思路 JDK1.7 起,原本存放在永久代的字符串常量池被移至 Java 堆之中,所以在 JDK7 及以上版本,限制方法区的容量对该测试用例来说是毫无意义的。
    在 JDK1.8 以后,永久代便完全退出了历史舞台,元空间作为其替代者登场。
  2. 参数配置
<= JDK1.6
-XX:PermSize
-XX:MaxPermSize
>= JDK1.8
-XX:MetaspaceSize
-XX:MaxMetaspaceSize 元空间最大值,默认是-1,即不限制,或者说只受限于本地内存大小。

直接内存溢出

  1. 错误信息
java.lang.OutOfMemoryError
  1. 排查思路
    由直接内存导致的内存溢出,一个明显的特征是在 HeapDump 文件中不会看见有什么明显的异常情况,如果读者发现内存溢出之后产生的 Dump 文件很小,而程序中又直接或间接使用了 DirectMemory ( 典型的间接使用就是 NIO),那就可以考虑重点检查一下直接内存方面的原因了。
  2. 参数配置
-XX:MaxDirectMemorySize 默认与Java堆最大值(由-Xmx指定)一致

JDK 自带的分析工具

jps    JVM Process Status Tool    显示指定系统内所有的HotSpot虚拟机进程
jinfo  Configuration Info for Java    显示虚拟机配置信息
jmap   JVM Memory Map    生成虚拟机的内存转储快照,生成heapdump文件
jhat   JVM Heap Dump Browser    用于分析heapdump文件,它会建立一个HTTP/HTML服务器,让用户在浏览器上查看分析结果
jstat  JVM Statistics Monitoring Tool    用于收集Hotspot虚拟机各方面的运行数据
jstack JVM Stack Trace    显示虚拟机的线程快照

jstat

可以显示本地或者远程虛拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据,在没有 GUI 图形界面、只提供了纯文本控制台环境的服务器上,是运行期定位虚拟机性能问题的常用工具。

-class
监视类加载、卸载数量、总空间以及类装载所耗费的时间
-gc
监视Java堆状况,包括Eden区、2个Survivor区、老年代、永久代等的容量,已用空间,垃圾收集时间合计等信息
-gccapacity
监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间
-gcutil
监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause
与-gcutil功能--样,但是会额外输出导致上一次垃圾收集产生的原因
-gcnew
监视新生代垃圾收集状况
-gcnewcapacity
监视内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
-gcold
监视老年代垃圾收集状况
-gcoldcapacity
监视内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
-gcpermcapacity
输出永久代使用到的最大、最小空间
-compiler
输出即时编译器编译过的方法、耗时等信息
-printcompilation
输出已经被即时编译的方法

jstack

可以生成虚拟机当前时刻的线程快照 (-般称为 threaddump 或者 javacore 文件)。线程快照就是当前虚拟机内每 - 条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。线程出现停顿时通过 jstack 来查看各个线程的调用堆栈,就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源。

jstack -l 进程ID  
除堆栈外显示关于锁的附加信息

扫一扫,关注我

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册