《深入理解Java虚拟机》读书笔记2

 2019-12-22 10:45  阅读(883)
文章分类:JVM

一、JDK命令行工具

JDK命令行工具位于JDK的bin目录下,能在处理应用程序性能问题、定位故障时发挥很大的作用。

这些工具都比较小,因为这些命令行工具大多数是jdk\lib\tools.jar类库的一层简单包装而已。

注:tools.jar中的类库不属于Java的标准API;

如果需要监控运行于JDK 1.5的虚拟机之上的程序,在程序启动时要添加参数“-Dcom.sun.management.jmxremote”开启JMX管理功能,如果被监控程序运行于JDK1.6的虚拟机之上,那JMX管理默认是开启的,不需要在虚拟机启动时添加任何参数。

下图列举了JDK主要命令行监控工具及用途:

2019120001267\_1.png

1、jps:虚拟机进程状况工具

功能:可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class, main()函数所在的类)的名称,以及这些进程的本地虚拟机的唯一ID(LVMID, Local Virtual Machine Identifier)。

注:对于本地虚拟器进程来说,LVMID与操作系统的进程ID(PID, Process Identifier)是一致的。

jps命令格式:

jps [options] [hostid]

主要选项:

-q :只输出LVMID, 省略主类的名称

-m : 输出虚拟机进程启动时传递给主类main()函数的参数

-l :输出主类的全名,如果进程执行的是Jar包,输出Jar路径

-v : 输出虚拟机进程启动时JVM参数

示例:

CMD进入Java的安装目录下的bin目录,输入:jps -l,会输出LVMID 和主类的全名。类似下面这样:

 
  1. 6536
  2. 6380 gui.Button1
  3. 6188 sun.tools.jps.Jps

2、jstat:虚拟机统计信息监视工具

jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在只有控制台环境时,它将是运行期定位虚拟机性能问题的首选工具。

jstat命令格式:

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

注:其中VMID:如果是本地虚拟机进程,VMID与LVMID是一致的,如果是远程虚拟机进程,那么VMID的格式应当是:

[protocol:] [//] lvid [@hostname[:port] /servername]

参数interval和count代表查询间隔和次数。如果省略这两个参数,说明只查询一次,假设需要每250毫秒查询一次进程2764垃圾收集的状况,一共查询20次,那命令应当是:

jstat -gc 2764 250 20

option选项:该选项代表用户希望查询的虚拟机信息,主要分为3类:类装载、垃圾收集和运行期编译状态。具体选项及作用如下:

2019120001267\_2.png

3、jinfo :Java 配置信息工具

jinfo(Configuration Info for Java)的作用是实时地查看和调整虚拟机的各项参数。

注:JDK1.6中,jinfo对于Windows平台的功能仍然有较大的限制,只提供了最基本的 -flag选项。

jinfo命令格式:jinfo [ option ] pid

例如:查询CMSInitiatingOccupancyFraction参数值:jinfo -flag CMSInitiatingOccupancyFraction 8088

4、jmap : Java内存映像工具

jmap(Memory Map for Java)命令用于生成堆转储快照(一般称为heapdump或dump文件)。

如果不使用jmap命令,可以通过指定虚拟机参数来获得dump文件,例如:指定-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在OOM异常出现之后自动生成dump文件,通过-XX:+HeapDumpOnCtrlBreak参数则可以使用【Ctrl】+ 【Break】键让虚拟机生成dump文件。或者Linux下通过Kill -3命令。

jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列,Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。

与jinfo命令一样,jmap在windows平台下有些功能是受限的,可以使用的是-dump选项来获取dump文件、-histo选项用来查看每个类的实例、空间占用统计等。

jmap命令格式:

jmap [ option ] vmid

option选项:

2019120001267\_3.png

5、jhat:虚拟机堆转储快照分析工具(dump分析工具)

通过jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看。

jhat工具很少使用,因为有比它强大的专业分析dump的工具,比如VisualVM、Eclipse Memory Analyzer、IBM HeapAnalyzer等工具。

6、jstack : Java 堆栈跟踪工具

jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因。

jstack命令格式:

jstack [ option ] vmid

option选项的定义如下:

2019120001267\_4.png

二、JDK可视化工具

1、JConsole :Java监视与管理控制台

JConsole(Java Monitoring and Management Console)是一款基于JMX的可视化监视和管理的工具。

2、VisualVM:多合一故障处理工具

VisualVM(All-in-One Java Troubleshooting Tool)是目前随JDK发布的功能最强大的运行监控和故障处理程序。

它的一个重要优点是:不需要被监视的程序基于特殊Agent运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中 。

它需要装扩展插件才能发挥它的功能,插件都以.nbm为后缀的包,安装插件:点击“工具”-> “插件”-> “已下载”菜单。

以下是插件的介绍:

** BTrace:动态日志跟踪

它的作用是在不停止目标程序运行的前提下,通过HotSpot虚拟机的HotSwap技术动态加入原本并不存在的调试代码,这可以在不停止部署的程序的条件下调试问题。

BTrace的用法还有很多,打印调用堆栈、参数、返回值只是最基本的应用,在它的网站上有使用BTrace进行性能监视、定位连接泄漏、内存泄漏、解决多线程竞争问题等的使用例子。

三、故障处理

1、案例一:高性能硬件上的程序部署策略

在高性能硬件上部署程序,目前主要有两种方式:

** 通过64位JDK来使用大内存

** 使用若干个32位虚拟机建立逻辑集群来利用硬件资源

通过第一种方式,即64位JDK来管理大内存,需要考虑下面可能的问题:

** 内存回收导致的长时间停顿;(一次Full GC会花费较长时间)

** 现阶段,64位JDK的性能测试结果普遍低于32位JDK

** 需要保证程序足够稳定,因为这种应用要是产生堆溢出几乎就无法产生堆转储快照(十几GB的dump文件),即使产生了也无法分析;

** 相同的程序在64位JDK中消耗的内存一般比32位JDK大,这是由于指针膨胀及数据类型对其补白等因素导致的。

通过第二种方式,具体做法是在一台物理机器上启动多个应用服务器进程,给每个服务器进程分配不同的端口,然后在前端搭建一个负载均衡器,以反向代理的方式来分配访问请求。这种方式需要考虑的问题是:

** 尽量避免节点竞争全局的资源,最典型的就是磁盘竞争

** 很难最高效率地利用某些资源池,譬如连接池

&sp; ** 各个节点仍然不可避免地受到32位的内存限制

** 大量使用本地缓存的应用,在逻辑集群中会造成较大的内存浪费,因为每个逻辑节点上都有一份缓存,可以考虑把本地缓存改为集中式缓存。

2、集群间同步导致的内存溢出

3、堆外内存导致的溢出错误

关于Direct Memory的垃圾回收:垃圾收集进行时,虚拟机会对Dircet Memory进行回收,但是它不会在发现Dircet Memory空间不足时就通知收集器进行垃圾回收,它只能等待老年代满了后Full GC,然后“顺便地”帮它清理掉内存的废弃对象。或者等到抛出内存溢出异常时,先chatch掉,再在catch块里面进行System.gc()。如果此时虚拟机打开了-XX:+DisableExplicitGC开关,那么仍然是不能清理Dircet Memory空间的。

从实践角度出发,除了Java堆和永久代之外,我们要注意以下区域也可能占用较多的内存:这里所有的内存总和会受到操作系统进程最大内存的限制:

** 、Direct Memory: 可通过-XX:MaxDirectMemorySize调整大小,内存不足时抛出OutOfMemoryError或OutOfMemoryError:Direct buffer memory。

&sp; **、线程堆栈:可通过-Xss调整大小,内存不足时抛出StackOverflowError(纵向无法分配,即无法分配新的栈帧)或OutOfMemoryError:unable to create new native thread(横向无法分配,即无法建立新的线程)。

**、Socket缓存区:每个Socket连接都Receive和Send两个缓存区,分别占大约37KB和25KB的内存,连续多的话这块内存占用也比较可观。如果无法分配,则可能会抛出IOException:Too many open files异常。

**、JNI代码:如果代码中使用JNI调用本地库,那本地库使用的内存也不在堆中。

**、虚拟机和GC:虚拟机和GC的代码执行也要消耗一定的内存。

4、外部命令导致系统缓慢

每个用户请求的处理都需要执行一个外部shell脚本来获取系统的一些信息。执行这个shell脚本是通过Java的Runtime.getRuntime().exec()方法来调用的,这种调用方式可以达到目的,但是它在Java虚拟机中非常消耗资源,即使外部命令本身能很快执行完毕,频繁调用时创建进程的开销也非常可观。

原因:Java虚拟机执行这个命令的过程是:首先克隆一个和当前虚拟机拥有一样环境变量的进程,再用这个新的进程去执行外部命令,最后再退出这个进程。频繁执行此过程,系统消耗会很大,不仅是CPU,内存的负担也很重。

解决方法:去掉shell脚本执行的语句,改为使用Java的API去获取系统信息。

5、服务器JVM进程崩溃

四、调优实战

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 《深入理解Java虚拟机》读书笔记2

相关推荐