【十六】JVM虚拟机运行时数据区、虚拟机监控工具

本篇是对《深入理解Java虚拟机----JVM高级特性与最佳实践》周志明作的第二版对应内容的一个读书笔记。

一、JVM运行时数据区 

名字线程共享/私有存的什么可能会报什么异常
程序计数器线程私有

如果正在执行java方法,存的字节码指令的地址

如果正在执行native方法,程序计数器为空。

虚拟机规范中未规定
Java虚拟机栈线程私有

每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

方法栈帧放入虚拟机栈中。

1.线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常

2.如果扩展时无法申请到足够内存,抛出OutOfMemoryError异常

本地方法栈线程私有跟Java虚拟机栈类似,区别在于Java虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用Native方法服务。同上
线程共享存放对象实例。是垃圾收集器管理的主要区域。如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常
方法区线程共享存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。如果方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常
运行时常量池线程共享方法区的一部分。运行期间也可能将新的常量放入常量池中,这种特性的使用常见于String类的intern()方法同上

二、各个区域出现内存溢出的定位、解决思路

2.1.程序计数器

该区域一般不会出现内存溢出,它占用的内存很小很小的,虚拟机规范中也并未指定它会跑什么异常。

2.2.栈溢出

HotSpot虚拟机并不区分java虚拟机栈和本地方法栈。

上面说了

每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

方法栈帧放入虚拟机栈中。

线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常

如果扩展时无法申请到足够内存,抛出OutOfMemoryError异常

有1个参数设置栈空间大小

-Xss 

溢出时日志报错的样子:

statck length:2402

Exception in thread "main" java.lang.StackOverflowError

            at org.fenixsoft.oom.VMStackSOF.leak(xxxxxx.java:xx)

另一种

Exception in thead "main" java.lang.OutOfMemoryError: unable to create new native thread

栈溢出定位、解决思路:

1.在单个线程下:无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法分配的时候虚拟机抛出的都是StackOverflowError异常。这样是可以把栈内存设置大的

2.在多线程下:为每个线程的栈分配的内存越大,反而越容易产生栈内存溢出,抛出OutOfMemoryError异常。

原因:在操作系统分配给每个进程的内存是有限的情况下。Java进程的内存=堆+栈+方法区+程序计数器。

换言之,在堆、方法区、程序计数器内存固定的情况下,剩下的内存都被栈瓜分了。

而每个线程分到的栈内存越大、可以创建的线程数量就越小,在创建新线程时就越容易栈溢出。

所以:如果是因为建立过多的线程导致栈内存溢出,在不减少线程数的情况下,只能通过减少最大堆、减少栈容量来换取更多的线程。

2.3.堆溢出

上面说了,堆是用来放对象的。如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

有3个参数跟设置堆空间和定位溢出有关

1.-Xms参数,设置堆的最小值

2.-Xmx参数,设置堆的最大值

如果最小值跟最大值一样,则避免了堆自动扩展

3.-XX:+HeapDumpOnOutOfMemoryError 设置虚拟机在出现内存溢出异常时Dump出当天内存堆转储快照,一遍事后分析。

堆溢出时日志报错的样子:

java.lang.OutOfMemoryError:Java heap space

Dumping heap to java_pidxxxx.hprof ...

Heap dump file created [xxxxx bytes in xxxx secs] 

堆溢出定位、解决思路:

1.判断是内存溢出(Memory Overflow)还是内存泄漏(Memory Leak):先通过内存映像(jmap能生成堆内存映像)分析工具(比如jhat、VisualVM、Eclipse Memory Analyzer、IBM HeapAnalyzer)对Dump出来的堆转储快照进行分析,确认内存中的对象是否是必要的

比如:MAT内存泄露分析(一)

2.如果是内存泄漏,通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的路径与GC Roots关联并导致垃圾回收器无法自动回收他们。定位出泄漏代码的位置,修改代码。

3.如果不是内存泄漏。看能不能把堆空间调大。检查代码,是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

2.4.方法区溢出

上面说了,方法区存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。如果方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

方法区空间大小的设置参数取决去分代,不同版本的JDK分代不一样,等会写1.8的分代。

在HotSpot虚拟机中的永久代就是管理方法区的。

有2个参数设置方法区空间:

1.-XX:PermSize

2.-XX:MaxPermSize

方法区溢出时日志报错的样子:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

...

三、虚拟机性能监控与故障处理工具

命令行工具:

3.1 jps 虚拟机进程状况工具

作用:

列出了正在运行的虚拟机进程,有进程ID,和执行主类名称(main()函数在的类)

命令格式:

jsp [options] [hostid]

options参数选项:

参数选项作用
-q只输出进程ID,省略主类名称
-m输出启动时传递给主类main()方法的参数
-l输出主类全名,如果进程执行的是Jar包,则输出Jar包全路径
-v输出虚拟机进程启动时JVM参数

hostid: 可以通过RMI协议查询开启了RMI服务的远程虚拟机进程状态 ,hostid为RMI注册表中注册的主机名

3.2 jstat 虚拟机统计信息监视工具

作用:

显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据

命令格式:

jstat [ option vmid [interval[s|ms] [count]] ]

interval表示查询间隔

count表查询次数

vmid是进程ID,如果是远程虚拟机进程vmid的格式是[protocol:][//]lvmid[@hostname[:port]/servername]

option参数选项:

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

3.3 jinfo java配置信息工具

作用:

实时查看和调整虚拟机各项参数

命令格式:

jinfo [option] pid

option参数选项:

选项作用
-flag查看虚拟机参数
-flag name = value修改虚拟机参数
-sysprops把虚拟机进程的System.getProperties()的内容打印出来

3.4 jmap Java内存映像工具

作用:

生成堆内存转储快照。查询finalize执行队列、Java堆和永久代的详细信息。

除了jmap以外拿到堆内存映像转储快照的方式还有:

1.-XX:+HeapDumpOnOutOfMemoryError 设置该参数,让虚拟机在OOM异常出现后自动dump文件

2.-XX:+HeapDumpOnCtrlBreak 设置该参数,使用Ctrl+Break键让虚拟机生成dump文件

命令格式:

jmap [ option ] vmid

option参数选项:

选项作用
-dump生成Java堆转储快照。格式为: -dump:[live, ]format=b,file=<filename>,其中live子参数说明是否只dump出存活的对象
-finalizerinfo显示在F-Queue中等待Finalizer线程执行finalize方法的对象。只在Linux/Solaris平台下有效
-heap显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况等。只在Linux/Solaris平台下有效
-histo显示堆中对象统计信息,包括类、实例数量、合计容量
-permstata以ClassLoader为统计口径显示永久代内存状态。只在Linux/Solaris平台下有效
-F当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照。只在Linux/Solaris平台下有效

3.5 jhat 虚拟机堆转储快照分析工具

作用:

分析jmap生成的堆转储快照。

jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后可以在浏览器查看,一般是http://localhost:7000

不推荐使用jhat来分析堆转储快照,因为它太简陋了,一般推荐使用VisualVM或者Eclipse Memory Analyzer。

3.6 jstack Java堆栈跟踪工具

作用:

生成虚拟机当前时刻的线程快照,一般称为threaddump或者javacore文件。

线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定线程出现长时间卡顿的原因。

在另一篇博客中详细讲了这个jstack如果分析线程死锁,及生成的文件内容详情

【二十二】Java多线程之线程转储和分析(jstack)

命令格式:

jstack [option] vmid

option参数选项:

选项作用
-F当正常数据的请求不被响应时,强制输出线程堆栈
-l除堆栈外,显示关于锁的附加信息
-m如果调用到本地方法的话,可以显示C/C++的堆栈

可视化工具

3.7 Jconsole

作用:这是一种基于JMX的可视化监视、管理工具。它的管理部分的功能是针对JMX MBean进行管理。

包括了内存(堆和非堆、执行GC)、线程(每个线程、检测死锁)、类、CPU使用情况、虚拟机概要(JVM参数)、MBean、监控。

可监控远程虚拟机的。

打开方式:windows在JAVA_HOME\bin下的Jconsole.exe

列表中会有本地所有的java进程,选择一个,进入监控界面

3.8 VisualVM

作用:覆盖了Jconsole的所有功能,并且还能扩展,并且对应用程序的实际性能影响很小可以直接应用在生产环境中

基于NetBeans平台开发,具备了插件扩展功能,通过插件扩展支持可以做到:

1.显示虚拟机进程以及进程的配置、环境信息(类似于jps、jinfo的功能)

2.监视应用程序的CPU、GC、堆、方法区以及线程信息(类似于jstat、jstack的功能)

3.dump以及分析堆转储快照(类似于 jmap、jhat的功能)

4.方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法

5.离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建议一个快照,可以将快照发送开发者处进行BUG反馈

可监控远程虚拟机的。

修改插件中心地址:

http://Visualvm java.net/pluginscenters.html 已关闭

https://visualvm.github.io/plugins.html 目前迁移到这里了

点击工具----插件,在弹出界面中点击设置,把URL换成https://visualvm.github.io/archive/uc/8u40/updates.xml.gz

详细方法见这篇博客:jvisualvm安装visualgc插件(java.net网站已关闭)

打开方式:windows在JAVA_HOME\bin下的JvisualVM.exe

推荐安装的插件:

1.生成、浏览堆转储快照。在“监视”页签中点击堆Dump,会得到一个以[heapdump]开头的堆转储快照,在里面可以执行OQL语句进行堆转储快照分析。

2.分析程序性能。在Profiler页签中,可以做程序运行期间方法级的CPU执行时间分析以及内存分析。因为对程序运行性能有较大的影响,不建议在生产环境中使用这项功能。

3.BTrace动态日志跟踪在不停止目标程序的前提下,通过HotSpot虚拟机的HotSwap技术,动态加入原本并不存在的测试代码。

 前面两个本来就自带,只需要自己安装一下BTrace插件就好了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值