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

时间:2022-12-27 16:59:59

一、JDK命令行工具

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

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

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

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

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

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

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 和主类的全名。类似下面这样:

6536
6380 gui.Button1
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类:类装载、垃圾收集和运行期编译状态。具体选项及作用如下:

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


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选项:

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


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选项的定义如下:

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


二、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大,这是由于指针膨胀及数据类型对其补白等因素导致的。

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

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

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

       ** 各个节点仍然不可避免地受到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。

       **、线程堆栈:可通过-Xss调整大小,内存不足时抛出*Error(纵向无法分配,即无法分配新的栈帧)或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进程崩溃

     

四、调优实战