• 虚拟机性能监控与故障处理工具(深入理解java虚拟机三)


      JDK自带的工具可以方便的帮助我们处理一些问题,包括查看JVM参数,分析内存变化,查看内存区域,查看线程等信息。

      我们熟悉的有java.exe,javac.exe,javap.exe(偶尔用),jps.exe,jmap.exe....等,下面会详细介绍。分别在linux和windows下面介绍。windows与linux下面使用的都是JDK1.7.0_80

    windows下的jdk版本如下:

    $ java -version
    java version "1.7.0_80"
    Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

    linux下的JDK版本如下:

    [root@VM_0_12_centos ~]# java -version
    java version "1.7.0_80"
    Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

    1.工具简介

    1.1windows下面查看

    $ dir
    appletviewer.exe    java-rmi.exe  jrunscript.exe    pack200.exe
    apt.exe             javaw.exe     jsadebugd.exe     policytool.exe
    extcheck.exe        javaws.exe    jstack.exe        rmic.exe
    idlj.exe            jcmd.exe      jstat.exe         rmid.exe
    jabswitch.exe       jconsole.exe  jstatd.exe        rmiregistry.exe
    jar.exe             jdb.exe       jvisualvm.exe     schemagen.exe
    jarsigner.exe       jhat.exe      keytool.exe       serialver.exe
    java.exe            jinfo.exe     kinit.exe         servertool.exe
    javac.exe           jli.dll       klist.exe         tnameserv.exe
    javadoc.exe         jmap.exe      ktab.exe          unpack200.exe
    javafxpackager.exe  jmc.exe       msvcr100.dll      wsgen.exe
    javah.exe           jmc.ini       native2ascii.exe  wsimport.exe
    javap.exe           jps.exe       orbd.exe          xjc.exe

    2.linux下面查看工具

    [root@VM_0_12_centos bin]# ls
    appletviewer  javac           jconsole  jps         native2ascii  serialver
    apt           javadoc         jcontrol  jrunscript  orbd          servertool
    ControlPanel  javafxpackager  jdb       jsadebugd   pack200       tnameserv
    extcheck      javah           jhat      jstack      policytool    unpack200
    idlj          javap           jinfo     jstat       rmic          wsgen
    jar           java-rmi.cgi    jmap      jstatd      rmid          wsimport
    jarsigner     javaws          jmc       jvisualvm   rmiregistry   xjc
    java          jcmd            jmc.ini   keytool     schemagen

    其实上面的工具大部分都是jdk/lib/tools.jar 类库的一层包装而已,它们主要的功能代码是在tools.jar中实现的。

    2.常见工具简要使用

    研究常见的工具的使用。主要有下面工具:

    jps---JVM Process Status tool,显示指定系统内所有的HotSpot虚拟机进程

    jstat---JVM Statistics Monitoring tool,用于收集HotSpot虚拟机各方面的运行数据

    jinfo---Configuration Info For Java,显示虚拟机配置信息

    jmap---Memory Map for Java,生成虚拟机的内存转储快照(heapdump文件)

    jhat---JVM Heap Dump Browser,用于分析heapdump文件,它会建立一个http/html服务器。让用于可以在浏览器上查看分析结果

    jstack---Stack Trace For Java,显示虚拟机的线程快照。

    下面研究具体的使用。

    2.1   jps---虚拟机进程状况工具(常用)

       JDK的很多工具都参考了UNIX系统的命名方式,jps(JVM Process Status)是其中的典型。除了名字像JVM的ps之外,功能也和UNIX的ps差不多:可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main函数所在的类)名称以及这些进程的本地虚拟机唯一ID(Local  Vitural Machine Identifier,LVMID)。虽然功能比较单一,但它是使用最频繁的JDK命令行工具,因为其他工具的使用都需要依赖此工具识别处的LVMID作为输入。对于本地虚拟机进程来说,LVMID与操作系统的进行ID(Process Identifier,PID)是一致的,使用windows的任务管理器或者unix的ps命令也可以查询到虚拟机的LVMID,但是如果启动了多个虚拟机进程,无法根据名称定位时,那就只能根据jps来区分了。

    jps命令格式:

    jps [option] [hostid]

    例如:

    C:UsersAdministrator>jps -l
    2204 org.apache.catalina.startup.Bootstrap
    2084
    6596 sun.tools.jps.Jps

      jps可以通过RMI协议查询开启了RMI服务的远程虚拟机进程状态,hostid为RMI注册表中注册的主机名。jps的常见其他选项如下:

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

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

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

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

    自己在windows下面的测试:

    C:UsersAdministrator>jps -m -l -v
    9772 sun.tools.jps.Jps -m -l -v -Denv.class.path=.;%JAVA_HOME%lib;%JAVA_HOME%lib	ools.jar -Dapplication.home=C:Program FilesJavajdk1.7.0_80 -Xms8m
    2204 org.apache.catalina.startup.Bootstrap start -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:53302 -Dcatalina.base=E:xiangmu.metadata.pluginsorg.eclipse.wst.server.core	mp1 -Dcatalina.home=E:	omcatapache-tomcat-7.0.88 -Dwtp.deploy=E:xiangmu -Djava.endorsed.dirs=E:	omcatapache-tomcat-7.0.88endorsed -Dfile.encoding=UTF-8
    2084  -Dosgi.requiredJavaVersion=1.7 -Xms256m -Xmx1024m -Xloggc:gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Dsun.lang.ClassLoader.allowArraySyntax=true -XX:MaxPermSize=256m

    9772是jps命令自身,可见jps本身也是一个Java程序,其主类位于sun.tools.jps.Jps(该类位于jdk/lib/tools.jar)

    2204 是自己启动的一个tomcat程序,主类是Bootstrap,向main函数传递的参数是start,后面是一些设置

    2084是Eclipse的PID,后面跟着eclipse.ini中对eclipse的设置(这也可以用来判断eclipse启动参数)

    自己在linux下面的测试

    [root@VM_0_12_centos ~]# jps -m -l -v
    4211 sun.tools.jps.Jps -m -l -v -Denv.class.path=.:/opt/java/jdk1.7.0_80/lib:/opt/java/jdk1.7.0_80/jre/lib -Dapplication.home=/opt/java/jdk1.7.0_80 -Xms8m
    21991 org.apache.catalina.startup.Bootstrap start -Djava.util.logging.config.file=/opt/apache-tomcat/apache-tomcat-7.0.72/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.endorsed.dirs=/opt/apache-tomcat/apache-tomcat-7.0.72/endorsed -Dcatalina.base=/opt/apache-tomcat/apache-tomcat-7.0.72 -Dcatalina.home=/opt/apache-tomcat/apache-tomcat-7.0.72 -Djava.io.tmpdir=/opt/apache-tomcat/apache-tomcat-7.0.72/temp

    4211 是jps命令自身的pid,

    21991 是linux下面的一个tomcat的pid,其主类、传递的参数以及配置与上面类似。

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

      jstat(JVM Statistics Monitoring Machine)是用于监视虚拟机各种运行状态信息的工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JID编译等运行时数据,在没有GUI的服务器上,对于定位虚拟机性能问题非常重要。

      jstat命令格式为:

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

     

     对于命令格式中的VMID与LVMID需要特别说明一下:如果是本地虚拟机进程,VMID与LVMID是一致的,如果是远程虚拟机进程,那VMID的格式应当如下:

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

      参数interval和count代表查询的间隔和次数,如果省略这两个参数,说明只查询一次。假设需要每250ms查询一次pid为 2204 程序的垃圾收集状态,总共查询20次,那么命令应当是:

    jstat -gc 2204 250 20

      

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

    -class  监视类装载、卸载数量、总空间以及类装载所耗费的时间

    -gc  监视java堆状况,包括Eden区、两个suprivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息(实际在hotspot虚拟机中,永久代不属于堆内存,而且在JDK8已经被元空间MetaSpace所取代)

    -gccapacity  监视内容与-gc基本相同,但是输出主要关注的是Java堆各个区域使用的最大、最小空间

    -gcutil  监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的比例

    -gccause  监视内容与-gc基本相同,但是会额外输出导致上一致gc的原因。

    -gcnew  监视新生代GC状况

    -gcnewcapacity  监视内容与-gcnew基本相同,但是输出主要关注的是使用的最大、最小空间

    -gcold  监视老年代GC状况

    -gcoldcapacity  监视内容与-gcold基本相同,但是输出主要关注的是使用的最大、最小空间

    -gcpermcapacity  输出永久代最大、最小空间

    -compiler  输出JIT编译过的方法、耗时等信息

    -printcompilation  输出已经被JIT编译的方法

    例如自己windows下面的测试

    C:UsersAdministrator>jstat -gcutil 2204
      S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
      0.00   0.00  33.52  54.34  55.99     15    0.322     1    0.282    0.604

      实验结果表明:这台虚拟机的新生代Enen区(E表示Eden)使用了33.52%空间,两个Suprivor区(SO和S1)都是空的,老年代Old(o表示Old)占用了54.34%,永久代(Perm)占用了55.99%。程序运行以爱共发生Minor GC(YGC表示Young GC)15次,总耗时0.322秒。发生FullGC(FGC)1次,FullGC总耗时0.282秒,所有GC耗时(GCT) 0.604秒。

    下面是一些其他测试:

    C:UsersAdministrator>jstat -gccause 2204
      S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
      0.00   0.00  33.52  54.34  55.99     15    0.322     1    0.282    0.604 Allocation Failure   No GC
    
    C:UsersAdministrator>jstat -class 2204
    Loaded  Bytes  Unloaded  Bytes     Time
      7880 15537.3        0     0.0      13.07
    
    C:UsersAdministrator>jstat -gc 2204
     S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
    43520.0 44544.0  0.0    0.0   499200.0 172323.0  120320.0   65384.2   83456.0 46728.6     15    0.322   1      0.282    0.604
    
    C:UsersAdministrator>jstat -compiler 2204
    Compiled Failed Invalid   Time   FailedType FailedMethod
        1504      1       0    17.19          1 org/apache/catalina/loader/WebappClassLoaderBase findResourceInternal
    
    C:UsersAdministrator>jstat -gccapacity 2204
     NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC      PGCMN    PGCMX     PGC       PC     YGC    FGC
     43008.0 686080.0 684544.0 43520.0 44544.0 499200.0    85504.0  1372160.0   120320.0   120320.0  21504.0  83968.0  83456.0  83456.0     15     1

    自己在linux下面的测试:

    [root@VM_0_12_centos ~]# jstat -gcutil 21991
      S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
      0.00   9.81  36.74  64.58  99.78    931    8.255    17    2.609   10.865
    [root@VM_0_12_centos ~]# jstat -gccause 21991
      S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
      0.00   9.81  38.73  64.58  99.78    931    8.255    17    2.609   10.865 Allocation Failure   No GC
    [root@VM_0_12_centos ~]# jstat -class 21991
    Loaded  Bytes  Unloaded  Bytes     Time
     12028 24837.3       48    70.8      66.16
    [root@VM_0_12_centos ~]# jstat -compiler 21991
    Compiled Failed Invalid   Time   FailedType FailedMethod
        2543      2       0    71.49          1 org/apache/jasper/xmlparser/ParserUtils convert

    一些具体的参数解释如下:

    S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
    
    S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
    
    S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
    
    S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
    
    EC:年轻代中Eden(伊甸园)的容量 (字节)
    
    EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
    
    OC:Old代的容量 (字节)
    
    OU:Old代目前已使用空间 (字节)
    
    PC:Perm(持久代)的容量 (字节)
    
    PU:Perm(持久代)目前已使用空间 (字节)
    
    YGC:从应用程序启动到采样时年轻代中gc次数
    
    YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
    
    FGC:从应用程序启动到采样时old代(全gc)gc次数
    
    FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
    
    GCT:从应用程序启动到采样时gc用的总时间(s)
    
    NGCMN:年轻代(young)中初始化(最小)的大小 (字节)
    
    NGCMX:年轻代(young)的最大容量 (字节)
    
    NGC:年轻代(young)中当前的容量 (字节)
    
    OGCMN:old代中初始化(最小)的大小 (字节)
    
    OGCMX:old代的最大容量 (字节)
    
    OGC:old代当前新生成的容量 (字节)
    
    PGCMN:perm代中初始化(最小)的大小 (字节)
    
    PGCMX:perm代的最大容量 (字节)
    
    PGC:perm代当前新生成的容量 (字节)
    
    S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
    
    S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
    
    E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
    
    O:old代已使用的占当前容量百分比
    
    P:perm代已使用的占当前容量百分比
    
    S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)
    
    S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节)
    
    ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)
    
    DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满)
    
    TT: 持有次数限制
    
    MTT : 最大持有次数限制

    2.3 jinfo  Java配置信息工具

      jinfo(Configuration Info for Java)的作用是 实时查看和调整虚拟机的各项参数。使用jps -v 可以查看虚拟器启动时候显示指定的参数列表,但是如果想查找未被显示指定的参数的系统的默认值,除了去查阅资料,就只能使用jinfo的 -flag选项进行查询了(如果只限于JDK版本6以上,使用Java -XX:+PrintFlagsFinal 查看默认值也是一个很好的选择),jinfo还可以使用 -sysprops选项把虚拟机进程的System.getProperties()的内容打印出来,JDK1.6之后,jinfo在windows和linux平台都提供,并且加入了运行期修改参数的能力,可以使用 -flag [+|-] name 或者 -flag name=value修改一部分运行期可写的虚拟机参数值。JDK1.6中,jinfo对于windows平台功能仍然有较大限制,只提供了最基本的-flag选项。

    jinfo的命令格式如下:

    C:UsersAdministrator>jinfo -flag
    Usage:
        jinfo [option] <pid>
            (to connect to running process)
        jinfo [option] <executable <core>
            (to connect to a core file)
        jinfo [option] [server_id@]<remote server IP or hostname>
            (to connect to remote debug server)
    
    where <option> is one of:
        -flag <name>         to print the value of the named VM flag
        -flag [+|-]<name>    to enable or disable the named VM flag
        -flag <name>=<value> to set the named VM flag to the given value
        -flags               to print VM flags
        -sysprops            to print Java system properties
        <no option>          to print both of the above
        -h | -help           to print this help message

    例如:

    (1)查看一个JVM的配置信息,也就是System.getProperties可以获取的信息:

    C:UsersAdministrator>jinfo 2084
    Attaching to process ID 2084, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 24.80-b11
    Java System Properties:
    
    java.vendor = Oracle Corporation
    osgi.bundles.defaultStartLevel = 4
    org.eclipse.debug.ui.breakpoints.toggleFactoriesUsed = false
    org.osgi.supports.framework.extension = true
    sun.management.compiler = HotSpot 64-Bit Tiered Compilers
    eclipse.p2.profile = epp.package.jee
    os.name = Windows 8
    ...

     或者

    C:UsersAdministrator>jinfo -sysprops 2084
    Attaching to process ID 2084, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 24.80-b11
    java.vendor = Oracle Corporation
    osgi.bundles.defaultStartLevel = 4
    org.eclipse.debug.ui.breakpoints.toggleFactoriesUsed = false
    org.osgi.supports.framework.extension = true
    sun.management.compiler = HotSpot 64-Bit Tiered Compilers
    eclipse.p2.profile = epp.package.jee
    os.name = Windows 8
    ...

    (2)查看JVM的启动参数信息

    C:UsersAdministrator>jinfo -flags 2084
    Attaching to process ID 2084, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 24.80-b11
    
    -Dosgi.requiredJavaVersion=1.7 -Xms256m -Xmx1024m -Xloggc:gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Dsun.lang.ClassLoader.allowArraySyntax=true -XX:MaxPermSize=256m

    (3)查看是否开启了GC日志的打印:

    C:UsersAdministrator>jinfo -flag PrintGC 2084
    -XX:+PrintGC
    
    C:UsersAdministrator>jinfo -flag PrintGCDetails 2084
    -XX:+PrintGCDetails

    (4)关闭GC打印功能

    C:UsersAdministrator>jinfo -flag -PrintGC 2084
    
    C:UsersAdministrator>jinfo -flag -PrintGCDetails 2084
    
    C:UsersAdministrator>jinfo -flag PrintGC 2084
    -XX:-PrintGC
    
    C:UsersAdministrator>jinfo -flag PrintGCDetails 2084
    -XX:-PrintGCDetails

    有一点必须注意:PrintGC必须开启,只开启PrintGCDetails、PrintGCTimeStamps不会输出GC,必须PrintGC同时开启

    (5)查看启动参数

    C:UsersAdministrator>jinfo -flag MaxHeapSize 2084
    -XX:MaxHeapSize=1073741824
    
    C:UsersAdministrator>jinfo -flag PermSize 2084
    -XX:PermSize=21757952

    2.4  jmap  Java内存映像工具  (常用)

      jmap(Memory map for java)命令用于生成堆转储快照(一般称为heapdump或dump文件),如果不使用jmap命令,想要获取java堆转储快照,还有一些比较暴力的手段:比如说用 -XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在发生OOM异常之后自动生成dump文件,通过 -XX:+HeapDumpOnCtrlBreak参数则可以使用 [Ctrl]+[Break]键让虚拟机生成dump文件,又或者在linux下面通过  Kill -3 命令发送进程退出信号"吓唬"一下虚拟机,也能拿到dump文件。

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

      和jinfo一样,jmap有不少功能在windows平台下都是受限的,除了生成dump文件的-dump选项和用于查看每个类的实例、空间占用的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris下使用。

      jmap的命令格式:

    C:UsersAdministrator>jmap
    Usage:
        jmap [option] <pid>
            (to connect to running process)
        jmap [option] <executable <core>
            (to connect to a core file)
        jmap [option] [server_id@]<remote server IP or hostname>
            (to connect to remote debug server)
    
    where <option> is one of:
        <none>               to print same info as Solaris pmap
        -heap                to print java heap summary
        -histo[:live]        to print histogram of java object heap; if the "live"
                             suboption is specified, only count live objects
        -permstat            to print permanent generation statistics
        -finalizerinfo       to print information on objects awaiting finalization
        -dump:<dump-options> to dump java heap in hprof binary format
                             dump-options:
                               live         dump only live objects; if not specified,
                                            all objects in the heap are dumped.
                               format=b     binary format
                               file=<file>  dump heap to <file>
                             Example: jmap -dump:live,format=b,file=heap.bin <pid>
        -F                   force. Use with -dump:<dump-options> <pid> or -histo
                             to force a heap dump or histogram when <pid> does not
                             respond. The "live" suboption is not supported
                             in this mode.
        -h | -help           to print this help message
        -J<flag>             to pass <flag> directly to the runtime system

    (1)windows下面的测试

    • 查看JVM的内存信息:
    C:UsersAdministrator>jmap -heap 2084  #查看堆内存
    Attaching to process ID 2084, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 24.80-b11
    
    using thread-local object allocation.
    Parallel GC with 4 thread(s)
    
    Heap Configuration:
       MinHeapFreeRatio = 0
       MaxHeapFreeRatio = 100
       MaxHeapSize      = 1073741824 (1024.0MB)
       NewSize          = 1310720 (1.25MB)
       MaxNewSize       = 17592186044415 MB
       OldSize          = 5439488 (5.1875MB)
       NewRatio         = 2
       SurvivorRatio    = 8
       PermSize         = 21757952 (20.75MB)
       MaxPermSize      = 268435456 (256.0MB)
       G1HeapRegionSize = 0 (0.0MB)
    
    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 295698432 (282.0MB)
       used     = 12552440 (11.970939636230469MB)
       free     = 283145992 (270.02906036376953MB)
       4.245014055400875% used
    From Space:
       capacity = 13107200 (12.5MB)
       used     = 13080592 (12.474624633789062MB)
       free     = 26608 (0.0253753662109375MB)
       99.7969970703125% used
    To Space:
       capacity = 10485760 (10.0MB)
       used     = 0 (0.0MB)
       free     = 10485760 (10.0MB)
       0.0% used
    PS Old Generation
       capacity = 541589504 (516.5MB)
       used     = 211967328 (202.14779663085938MB)
       free     = 329622176 (314.3522033691406MB)
       39.13800515602311% used
    PS Perm Generation
       capacity = 166199296 (158.5MB)
       used     = 166122072 (158.42635345458984MB)
       free     = 77224 (0.07364654541015625MB)
       99.95353530258035% used
    
    44318 interned Strings occupying 4390312 bytes.
    • 查看一个JVM中的存活(加上 :live 会只显示存活对象)对象:(我将信息重定向到一个文件中了,存活对象太多,篇幅太长)
    C:UsersAdministrator>jmap -histo:live 2084 >> tt.txt

    打开文件查看结果:最后有3068个类   (我们也可以发现pring的单例模式,确实使用了CGLIB代理模式生成唯一的代理对象)

     

    •  生成Java堆转储快照文件(对文件的分析在后面介绍)
    C:UsersAdministrator>jmap -dump:live,format=b,file=heap.bin  2204
    Dumping heap to C:UsersAdministratorheap.bin ...
    Heap dump file created

    (2)linux下面的测试:

    [root@VM_0_12_centos ~]# jmap -heap 21991  #查看内存JVM参数
    Attaching to process ID 21991, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 24.80-b11
    
    using thread-local object allocation.
    Mark Sweep Compact GC
    
    Heap Configuration:
       MinHeapFreeRatio = 40
       MaxHeapFreeRatio = 70
       MaxHeapSize      = 482344960 (460.0MB)
       NewSize          = 1310720 (1.25MB)
       MaxNewSize       = 17592186044415 MB
       OldSize          = 5439488 (5.1875MB)
       NewRatio         = 2
       SurvivorRatio    = 8
       PermSize         = 21757952 (20.75MB)
       MaxPermSize      = 85983232 (82.0MB)
       G1HeapRegionSize = 0 (0.0MB)
    
    Heap Usage:
    New Generation (Eden + 1 Survivor Space):
       capacity = 78053376 (74.4375MB)
       used     = 12557464 (11.975730895996094MB)
       free     = 65495912 (62.461769104003906MB)
       16.088303470691645% used
    Eden Space:
       capacity = 69402624 (66.1875MB)
       used     = 11711400 (11.168861389160156MB)
       free     = 57691224 (55.018638610839844MB)
       16.874578113934135% used
    From Space:
       capacity = 8650752 (8.25MB)
       used     = 846064 (0.8068695068359375MB)
       free     = 7804688 (7.4431304931640625MB)
       9.780236446496213% used
    To Space:
       capacity = 8650752 (8.25MB)
       used     = 0 (0.0MB)
       free     = 8650752 (8.25MB)
       0.0% used
    tenured generation:
       capacity = 173322240 (165.29296875MB)
       used     = 111934032 (106.74861145019531MB)
       free     = 61388208 (58.54435729980469MB)
       64.58145936724566% used
    Perm Generation:
       capacity = 77332480 (73.75MB)
       used     = 77163160 (73.5885238647461MB)
       free     = 169320 (0.16147613525390625MB)
       99.78104930813029% used
    
    30082 interned Strings occupying 3502776 bytes.
    [root@VM_0_12_centos ~]# jmap -histo:live 21991 >> exam.info  #查看存活对象信息,重定向到文件中
    [root@VM_0_12_centos ~]# jmap -dump:live,format=b,file=heap.bin  21991  #查看堆转储快照信息
    Dumping heap to /root/heap.bin ...
    Heap dump file created

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

      jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。jhat内置了一个小型的HTTP/HTML分析器,生成dump文件的分析结果之后,可以在浏览器查看。一般也用不到这个工具,主要原因有:一是一般不会在部署应用程序的服务器上直接生成dump文件,即使这样做了,也会尽量将dump文件复制到其他机器进行分析,因为分析工作是一个耗时且消耗硬件资源的工作;二是此工具的分析功能相对比较简陋,比较好的有Visual VM,以及专业用于分析dump文件的Eclipse Memory Analyzer等。

      接下来用jhat分析上面jmap生成的dump快照信息:

    C:UsersAdministrator>jhat heap.bin
    Reading from heap.bin...
    Dump file created Mon Dec 03 15:04:02 CST 2018
    Snapshot read, resolving...
    Resolving 816411 objects...
    Chasing references, expect 163 dots...................................................................................................................................................................
    Eliminating duplicate references...................................................................................................................................................................
    Snapshot resolved.
    Started HTTP server on port 7000
    Server is ready.

      可以看到上面在端口7000,建立了http监听,输入http://localhost:7000/ 即可查看分析信息

       

      分析结果默认是以包为单位分组显示,分析内存泄漏问题主要会使用其中的"heap histogram"(与jmap -histo)功能一样可以查看类的统计信息与OQL页签的功能,前者可以找到内存中总容量最大的对象,后者是标准的对象查询语言,使用类似SQL的语法对内存中的对象进行查询统计(这个参考深入JVM虚拟机一书可以方便使用)。

    查看heap histogram

    点一个类进去再次查看:(这里也解释了spring的cglib代理功能基于继承方式实现)

     再次点击DepartmentServiceImpl查看:

     2.6 jstack  java堆栈跟踪工具

      jstack(Stack Trace For Java)命令用于生成虚拟机当前时刻的线程快照(一般成为threaddump或者javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的是为了定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源等待的时间过长等原因。线程出现停顿的时候可以通过jstack来查看各个线程的调用堆栈,就可以知道响应的线程到底在后台做什么或者等待什么资源。

      jstack的命令格式如下:

    C:UsersAdministrator>jstack
    Usage:
        jstack [-l] <pid>
            (to connect to running process)
        jstack -F [-m] [-l] <pid>
            (to connect to a hung process)
        jstack [-m] [-l] <executable> <core>
            (to connect to a core file)
        jstack [-m] [-l] [server_id@]<remote server IP or hostname>
            (to connect to a remote debug server)
    
    Options:
        -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
        -m  to print both java and native frames (mixed mode)
        -l  long listing. Prints additional information about locks
        -h or -help to print this help message

    例如:

    C:UsersAdministrator>jstack -l 2204

      在JDK1.5中,java.lang.Thread类新增了一个getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个对象可以通过简单的几行代码就完成jstack的大部分功能,在实际项目中可以用这个做管理界面,可随时查看线程堆栈。如下代码:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ page import="java.util.Map"%>
    <html>
    <head>
    <title>服务器线程堆栈信息</title>
    </head>
    <body>
        <%
            for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
                Thread thread = (Thread) stackTrace.getKey();
                StackTraceElement[] stackTraceElements = stackTrace.getValue();
                if (thread.equals(Thread.currentThread())) {
                    continue;
                }
                out.print("
     线程->" + thread.getName() + "
    ");
                for (StackTraceElement s : stackTraceElements) {
                    out.print("	" + s + "
    ");
                }
            }
        %>
    </body>
    </html>

    补充:jstack也可以用来检测死锁

    一个死锁程序:

    package cn.xm.exam.test;
    
    public class test {
    
        public static void main(String[] args) {
            final Integer lock1 = 1;
            final Integer lock2 = 2;
            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    synchronized (lock1) {
                        try {
                            System.out.println("lock1-----");
                            Thread.sleep(2 * 1000);
                            synchronized (lock2) {
                                System.out.println("lock1-----lock2");
                            }
                        } catch (InterruptedException e) {
                        }
                    }
                }
            };
            new Thread(r1).start();
    
            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    synchronized (lock2) {
                        try {
                            System.out.println("lock2-----");
                            Thread.sleep(2 * 1000);
                            synchronized (lock1) {
                                System.out.println("lock2-----lock1");
                            }
                        } catch (InterruptedException e) {
                        }
                    }
                }
            };
            new Thread(r2).start();
        }
    }

    jps+jstack检测死锁:

    C:Usersliqiang>jps
    336164
    342476 test
    343376 Jps
    
    C:Usersliqiang>jstack 342476
    2018-12-10 21:19:47
    Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.80-b11 mixed mode):
    
    "DestroyJavaVM" prio=6 tid=0x00000000026a5800 nid=0x53a04 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Thread-1" prio=6 tid=0x000000000e97e800 nid=0x53134 waiting for monitor entry [0x000000000ef7f000]
       java.lang.Thread.State: BLOCKED (on object monitor)
            at cn.xm.exam.test.test$2.run(test.java:33)
            - waiting to lock <0x00000007c0af8478> (a java.lang.Integer)
            - locked <0x00000007c0af8488> (a java.lang.Integer)
            at java.lang.Thread.run(Thread.java:745)
    
    "Thread-0" prio=6 tid=0x000000000e97d800 nid=0x53b40 waiting for monitor entry [0x000000000ee7f000]
       java.lang.Thread.State: BLOCKED (on object monitor)
            at cn.xm.exam.test.test$1.run(test.java:16)
            - waiting to lock <0x00000007c0af8488> (a java.lang.Integer)
            - locked <0x00000007c0af8478> (a java.lang.Integer)
            at java.lang.Thread.run(Thread.java:745)
    
    "Service Thread" daemon prio=6 tid=0x000000000d04b000 nid=0x4e590 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread1" daemon prio=10 tid=0x000000000d03a000 nid=0x53594 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" daemon prio=10 tid=0x000000000d035000 nid=0x52bd8 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Attach Listener" daemon prio=10 tid=0x000000000d034000 nid=0x52d64 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" daemon prio=10 tid=0x000000000d030800 nid=0x531f0 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" daemon prio=8 tid=0x000000000278c800 nid=0x53868 in Object.wait() [0x000000000e37f000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00000007c0904858> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
            - locked <0x00000007c0904858> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
            at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
    
    "Reference Handler" daemon prio=10 tid=0x0000000002784800 nid=0x53adc in Object.wait() [0x000000000e27f000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00000007c0904470> (a java.lang.ref.Reference$Lock)
            at java.lang.Object.wait(Object.java:503)
            at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
            - locked <0x00000007c0904470> (a java.lang.ref.Reference$Lock)
    
    "VM Thread" prio=10 tid=0x000000000cff3000 nid=0x53ad0 runnable
    
    "GC task thread#0 (ParallelGC)" prio=6 tid=0x00000000026bb000 nid=0x53b08 runnable
    
    "GC task thread#1 (ParallelGC)" prio=6 tid=0x00000000026bc800 nid=0x53ac4 runnable
    
    "GC task thread#2 (ParallelGC)" prio=6 tid=0x00000000026be800 nid=0x53368 runnable
    
    "GC task thread#3 (ParallelGC)" prio=6 tid=0x00000000026c0000 nid=0x53acc runnable
    
    "VM Periodic Task Thread" prio=10 tid=0x000000000d08e800 nid=0x539a4 waiting on condition
    
    JNI global references: 111
    
    
    Found one Java-level deadlock:
    =============================
    "Thread-1":
      waiting to lock monitor 0x000000000278c3b8 (object 0x00000007c0af8478, a java.lang.Integer),
      which is held by "Thread-0"
    "Thread-0":
      waiting to lock monitor 0x000000000278af18 (object 0x00000007c0af8488, a java.lang.Integer),
      which is held by "Thread-1"
    
    Java stack information for the threads listed above:
    ===================================================
    "Thread-1":
            at cn.xm.exam.test.test$2.run(test.java:33)
            - waiting to lock <0x00000007c0af8478> (a java.lang.Integer)
            - locked <0x00000007c0af8488> (a java.lang.Integer)
            at java.lang.Thread.run(Thread.java:745)
    "Thread-0":
            at cn.xm.exam.test.test$1.run(test.java:16)
            - waiting to lock <0x00000007c0af8488> (a java.lang.Integer)
            - locked <0x00000007c0af8478> (a java.lang.Integer)
            at java.lang.Thread.run(Thread.java:745)
    
    Found 1 deadlock.

     3.JDK的可视化工具的使用 (只在windows下进行测试)

       JDK除了上面的大量的命令行工具,还提供了两个可视化工具,JConsole和Visual VM。JConsole是JDL5才有的,而Visual VM是JDK6的Update7中首次发布的,现在已经成为主流的多合一故障处理工具。

    3.1Jconsole   java监控与管理控制台

      JConsole(Java Monitoring and management Console)是基于JMX的可视化监视、管理工具。它管理部分的功能是针对JMX MBean进行管理,由于MBean可以使用代码、中间件服务器的管理控制台或者所有符合JMX规范的软件进行访问。

    1.启动JConsole

      双击Jdk/bin/jconsole.exe启动即可。启动之后会自动显示本地的LVMID,相当于JPS功能,双击一个打开即可。

      也可以使用下面的远程进程进行远程连接。

    2.启动之后查看结果:

       概述页签显示的是整个虚拟机运行时数据的概览,其中包括"堆使用情况"、"线程"、"类"、"CPU占用率等",这些曲线图是后面内存、线程、类页签的汇总。

    3.内存监控

       内存页签相当于可视化的jstat命令。(在这里也可以看出永久代属于非堆区)。也可以看到垃圾收集器以及垃圾收集时间等信息。

    4.线程监控 

      线程页签相当于jstack命令,遇到线程停顿时可以用这个命令进行分析。也可以进行死锁检测。

      

      下面模拟一个线程死锁的线程检测:(一个简单的双同步发生的线程死锁的例子)

    package cn.xm.exam.test;
    
    public class test {
    
        public static void main(String[] args) {
            final Integer lock1 = 1;
            final Integer lock2 = 2;
            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    synchronized (lock1) {
                        try {
                            System.out.println("lock1-----");
                            Thread.sleep(2 * 1000);
                            synchronized (lock2) {
                                System.out.println("lock1-----lock2");
                            }
                        } catch (InterruptedException e) {
                        }
                    }
                }
            };
            new Thread(r1).start();
    
            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    synchronized (lock2) {
                        try {
                            System.out.println("lock2-----");
                            Thread.sleep(2 * 1000);
                            synchronized (lock1) {
                                System.out.println("lock2-----lock1");
                            }
                        } catch (InterruptedException e) {
                        }
                    }
                }
            };
            new Thread(r2).start();
        }
    }

    再次检测死锁查看线程死锁信息:(可以看到多处一个死锁签,可以看到发生死锁的当前线程所拥有的锁以及等待的另一个锁被另一个线程锁所拥有)

    3.2  jvisualvm.exe  :多合一故障处理工具(重要 )

    1.启动

      位于jdk/bin/jvisualvm.exe,双击即可启动,启动也是会显示JPS类似的功能,双击一个PID进去即可。

    2.概述

      可以看到参数信息,以及一些配置信息。有jps -ml的功能。其系统属性的页签类似于jinfo功能。也可以看到JVM参数的设置信息。

     3.监视页签查看概述信息

      也可以dump出堆转储快照。

    执行堆dump之后可以进行分析类以及实例信息,类似于jmap -histo的功能 (重要) 。而且堆dump也可以另存为文件。

    4.利用JVisualVM分析死锁:(例子还是上面的死锁程序)

      会自动检测死锁。

     我们dump出快照查看信息:

    总结:  至此完成了工具的研究,最常用的是jps -lm   + jmap -heap pid   +    jmap -histo:live    。如果可以使用可视化工具的话jvisualVM是不错的选择。

                                      

    补充一些JVM中的知识,便于分析类以及其实例:

    在Java中,任何类型都有class,包括基本数据类型与void。在Java中 [   代表数组,  [[  代表二维数组。依次类推。

    其他的描述标识符如下:

    B---基本数据类型byte

    C---基本数据类型char

    D---基本数据类型double

    F---基本数据类型float

    I---基本数据类型int

    J---基本数据类型long

    S---基本数据类型short

    Z---基本数据类型boolean

    V---特殊类型void

    L---对象类型(也就是引用类型)。例如 Ljava/lang/Object.

    需要注意的是八种基本数据类型也有  calss,而且其对应包装类型有一个TYPE成员变量就是其基本数据类型。特殊类型void也有class。基本类型的数组与引用类型数组也都有class

    public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

    例如:

         System.out.println(int.class);
            System.out.println(Integer.class);
            System.out.println(Integer.TYPE);
    
            System.out.println(Integer[].class);
            System.out.println(int[].class);
            System.out.println(void.class);

     结果:

    int
    class java.lang.Integer
    int
    class [Ljava.lang.Integer;
    class [I
    void

     注意int不是对象,所以没有getClass方法。只有int.class

  • 相关阅读:
    无重复字符的最长子串
    有效的括号
    最长公共前缀
    罗马数字转整数
    Android解析JSON数据异步加载新闻图片
    回文数
    Java从Json获得数据的四种方式
    JavaMD5加密工具类
    div模仿select效果二:带搜索框
    BG雪碧图制作要求
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/10029719.html
Copyright © 2020-2023  润新知