Java命令学习

一、jps

功能

jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前全部java进程pid的命令,简单实用,很是适合在linux/unix平台上简单察看当前java进程的一些简单状况。html

原理

jdk中的jps命令能够显示当前运行的java进程以及相关参数,它的实现机制以下:
java程序在启动之后,会在java.io.tmpdir指定的目录下,就是临时文件夹里,生成一个相似于hsperfdata_User的文件夹,这个文件夹里(在windows中为C:\Users\dup\AppData\Local\Temp\hsperfdat_user),有几个文件,名字就是java进程的pid,所以列出当前运行的java进程,只是把这个目录里的文件名列一下而已。 至于系统的参数什么,就能够解析这几个文件得到。java

上面的内容就是我机器中C:\Users\dup\AppData\Local\Temp\hsperfdat_user目录下的内容,其中9160就是我机器上当前运行中的java的进程的pid,咱们执行jps验证一下:linux

执行了jps命令以后,咱们发现有两个java进程,一个是pid为9160的myeclipse运行的进程,另一个是pid为10176的jps使用的进程(他也是java命令,也要开一个进程)web

使用

想要学习一个命令,先来看看帮助,使用jps -help查看帮助:算法

接下来,为了详细介绍这些参数,咱们编写几个类,在main方法里写一个while(true)的循环,查看java进程状况。代码以下:数据库

package com.JavaCommand;
/**
 * @author dup
 */
public class JpsDemo {
    public static void main(String[] args) {
        while(true){
            System.out.println(1);
        }
    }
}

-q 只显示pid,不显示class名称,jar文件名和传递给main 方法的参数apache

-m 输出传递给main 方法的参数,在嵌入式jvm上多是null, 在这里,在启动main方法的时候,我给String[] args传递两个参数。hollis,chuang,执行jsp -m:ubuntu

-l 输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名windows

-v 输出传递给JVM的参数 在这里,在启动main方法的时候,我给jvm传递一个参数:-Dfile.encoding=UTF-8,执行jps -v浏览器

PS:jps命令有个地方很很差,彷佛只能显示当前用户的java进程,要显示其余用户的仍是只能用unix/linux的ps命令

JPS失效处理

现象: 用ps -ef|grep java能看到启动的java进程,可是用jps查看却不存在该进程的id。待会儿解释过以后就能知道在该状况下,jconsole、jvisualvm可能没法监控该进程,其余java自带工具也可能没法使用

分析: jps、jconsole、jvisualvm等工具的数据来源就是这个文件(C:\Users\dup\AppData\Local\Temp\hsperfdat_user\pid)。因此当该文件不存在或是没法读取时就会出现jps没法查看该进程号,jconsole没法监控等问题

缘由:

(1)、磁盘读写、目录权限问题 若该用户没有权限写C:\Users\dup\AppData\Local\Temp\hsperfdat_user\pid目录或是磁盘已满,则没法建立C:\Users\dup\AppData\Local\Temp\hsperfdat_user\pid\pid文件。或该文件已经生成,但用户没有读权限

(2)、临时文件丢失,被删除或是按期清理 对于linux机器,通常都会存在定时任务对临时文件夹进行清理,致使/tmp目录被清空。这也是我第一次碰到该现象的缘由。经常使用的可能定时删除临时目录的工具为crontab、redhat的tmpwatch、ubuntu的tmpreaper等等

这个致使的现象可能会是这样,用jconsole监控进程,发如今某一时段后进程仍然存在,可是却没有监控信息了。

(3)、java进程信息文件存储地址被设置,不在C:\Users\dup\AppData\Local\Temp\hsperfdat_user\目录下 上面咱们在介绍时说默认会在C:\Users\dup\AppData\Local\Temp\hsperfdat_user\目录保存进程信息,但因为以上一、2所述缘由,可能致使该文件没法生成或是丢失,因此java启动时提供了参数(-Djava.io.tmpdir),能够对这个文件的位置进行设置,而jps、jconsole都只会从C:\Users\dup\AppData\Local\Temp\hsperfdat_user目录读取,而没法从设置后的目录读物信息,这是我第二次碰到该现象的缘由

二、jstack

功能

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的缘由,如线程间死锁、死循环、请求外部资源致使的长时间等待等。 线程出现停顿的时候经过jstack来查看各个线程的调用堆栈,就能够知道没有响应的线程到底在后台作什么事情,或者等待什么资源。 若是java程序崩溃生成core文件,jstack工具能够用来得到core文件的java stack和native stack的信息,从而能够轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还能够附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 若是如今运行的java程序呈现hung的状态,jstack是很是有用的。jstack命令主要用来查看Java线程的调用堆栈的,能够用来分析线程问题(如死锁)。

线程状态

想要经过jstack命令来分析线程的状况的话,首先要知道线程都有哪些状态,下面这些状态是咱们使用jstack命令查看线程堆栈信息时可能会看到的线程的几种状态

NEW,未启动的。不会出如今Dump中。

RUNNABLE,在虚拟机内执行的。

BLOCKED,受阻塞并等待监视器锁。

WATING,无限期等待另外一个线程执行特定操做。

TIMED_WATING,有时限的等待另外一个线程的特定操做。

TERMINATED,已退出的。

Monitor

在多线程的 JAVA程序中,实现线程之间的同步,就要说说 Monitor。 Monitor是 Java中用以实现线程之间的互斥与协做的主要手段,它能够当作是对象或者 Class的锁。每个对象都有,也仅有一个 monitor。下 面这个图,描述了线程和 Monitor之间关系,以 及线程的状态转换图:

进入区(Entrt Set):表示线程经过synchronized要求获取对象的锁。若是对象未被锁住,则进入拥有者;不然则在进入区等待。一旦对象锁被其余线程释放,当即参与竞争。

拥有者(The Owner):表示某一线程成功竞争到对象锁。

等待区(Wait Set):表示线程经过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。

从图中能够看出,一个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是“Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是“Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。 先看 “Entry Set”里面的线程。咱们称被 synchronized保护起来的代码段为临界区。当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。对应的 code就像:

synchronized(obj) {
.........

}

调用修饰

表示线程在方法调用时,额外的重要的操做。线程Dump分析的重要信息。修饰上方的方法调用。

locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。

waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在迚入区等待。

waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放锁幵在等待区等待。

parking to wait for <地址> 目标

locked

at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at com.jiuqi.dna.core.internal.db.datasource.PooledConnection.prepareStatement

经过synchronized关键字,成功获取到了对象的锁,成为监视器的拥有者,在临界区内操做。对象锁是能够线程重入的。

waiting to lock

at com.jiuqi.dna.core.impl.CacheHolder.isVisibleIn(CacheHolder.java:165)
- waiting to lock <0x0000000097ba9aa8> (a CacheHolder)
at com.jiuqi.dna.core.impl.CacheGroup$Index.findHolder
at com.jiuqi.dna.core.impl.ContextImpl.find
at com.jiuqi.dna.bap.basedata.common.util.BaseDataCenter.findInfo

经过synchronized关键字,没有获取到了对象的锁,线程在监视器的进入区等待。在调用栈顶出现,线程状态为Blocked。

waiting on

at java.lang.Object.wait(Native Method)
- waiting on <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo
- locked <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingThread.run

经过synchronized关键字,成功获取到了对象的锁后,调用了wait方法,进入对象的等待区等待。在调用栈顶出现,线程状态为WAITING或TIMED_WATING。

parking to wait for

park是基本的线程阻塞原语,不经过监视器在对象上阻塞。随concurrent包会出现的新的机制,不synchronized体系不一样。

线程动做

线程状态产生的缘由

runnable:状态通常为RUNNABLE。

in Object.wait():等待区等待,状态为WAITING或TIMED_WAITING。

waiting for monitor entry:进入区等待,状态为BLOCKED。

waiting on condition:等待区等待、被park。

sleeping:休眠的线程,调用了Thread.sleep()。

Wait on condition 该状态出如今线程等待某个条件的发生。具体是什么缘由,能够结合 stacktrace来分析。 最多见的状况就是线程处于sleep状态,等待被唤醒。 常见的状况还有等待网络IO:在java引入nio以前,对于每一个网络链接,都有一个对应的线程来处理网络的读写操做,即便没有可读写的数据,线程仍然阻塞在读写操做上,这样有可能形成资源浪费,并且给操做系统的线程调度也带来压力。在 NewIO里采用了新的机制,编写的服务器程序的性能和可扩展性都获得提升。 正等待网络读写,这多是一个网络瓶颈的征兆。由于网络阻塞致使线程没法执行。一种状况是网络很是忙,几 乎消耗了全部的带宽,仍然有大量数据等待网络读 写;另外一种状况也多是网络空闲,但因为路由等问题,致使包没法正常的到达。因此要结合系统的一些性能观察工具来综合分析,好比 netstat统计单位时间的发送包的数目,若是很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,若是系统态的 CPU时间,相对于用户态的 CPU时间比例较高;若是程序运行在 Solaris 10平台上,能够用 dtrace工具看系统调用的状况,若是观察到 read/write的系统调用的次数或者运行时间遥遥领先;这些都指向因为网络带宽所限致使的网络瓶颈。http://www.blogjava.net/jzone/articles/303979.html

线程Dump的分析

原则

结合代码阅读的推理。须要线程Dump和源码的相互推导和印证。

形成Bug的根源每每会在调用栈上直接体现,必定格外注意线程当前调用以前的全部调用。

入手点

进入区等待

线程状态BLOCKED,线程动做wait for monitor entry,调用修饰waiting to lock老是一块儿出现。表示在代码级别已经存在冲突的调用。必然有问题的代码,须要尽量减小其发生。

同步块阻塞

一个线程锁住某对象,大量其余线程在该对象上等待。

持续运行的IO IO操做是能够以RUNNABLE状态达成阻塞。例如:数据库死锁、网络读写。 格外注意对IO线程的真实状态的分析。 通常来讲,被捕捉到RUNNABLE的IO调用,都是有问题的。

分线程调度的休眠

正常的线程池等待

"d&a-131" in Object.wait()
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo(WorkingManager.java:322)
- locked <0x0000000313f656f8> (a com.jiuqi.dna.core.impl.WorkingThread)
at com.jiuqi.dna.core.impl.WorkingThread.run(WorkingThread.java:40)

可疑的线程等待

"d&a-121" in Object.wait()
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at com.jiuqi.dna.core.impl.AcquirableAccessor.exclusive()
- locked <0x00000003011678d8> (a com.jiuqi.dna.core.impl.CacheGroup)
at com.jiuqi.dna.core.impl.Transaction.lock()

入手点总结

wait on monitor entry: 被阻塞的,确定有问题

runnable : 注意IO线程

in Object.wait(): 注意非线程池等待

使用

首先,咱们分析这么一段程序的线程状况:

package com.test;

public class JstackDemo {
	public static void main(String[] args) {
        while (true) {
            //Do Nothing
        }
    }
}

先是有jps查看进程号:

而后使用jstack 查看堆栈信息:

咱们能够从这段堆栈信息中看出什么来呢?咱们能够看到,当前一共有一条用户级别线程,线程处于runnable状态,执行到JStackDemo1.java的第5行。 看下面代码:

package com.test;

public class JstackDemo1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Thread1());
        thread.start();
    }
}
class Thread1 implements Runnable{
    @Override
    public void run() {
    	synchronized (Thread1.class) {
    		while(true){
    			System.out.println(1);
    			try {
					Thread1.class.wait();
				} catch (InterruptedException e) {
				}
    		}
		}
    }
}

线程堆栈信息以下:

咱们能看到:

线程的状态: WAITING 线程的调用栈 线程的当前锁住的资源: <0x04b9b7f0> 线程当前等待的资源:<0x04b9b7f0>

为何同时锁住的等待同一个资源:

线程的执行中,先得到了这个对象的 Monitor(对应于 locked <0x04b9b7f0>)。当执行到 obj.wait(), 线程即放弃了 Monitor的全部权,进入 “wait set”队列(对应于 waiting on <0x04b9b7f0> )

死锁分析

学会了怎么使用jstack命令以后,咱们就能够看看,如何使用jstack分析死锁了,这也是咱们必定要掌握的内容。 啥叫死锁? 所谓死锁: 是指两个或两个以上的进程在执行过程当中,因为竞争资源或者因为彼此通讯而形成的一种阻塞的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 说白了,我如今想吃鸡蛋灌饼,桌子上放着鸡蛋和饼,可是我和个人朋友同时分别拿起了鸡蛋和病,我手里拿着鸡蛋,可是我须要他手里的饼。他手里拿着饼,可是他想要我手里的鸡蛋。就这样,若是不能同时拿到鸡蛋和饼,那咱们就不能继续作后面的工做(作鸡蛋灌饼)。因此,这就形成了死锁。 看一段死锁的程序:

package com.test;

public class JstackDemo2 {
	public static void main(String[] args) {
        Thread t1 = new Thread(new DeadLockclass(true));//创建一个线程
        Thread t2 = new Thread(new DeadLockclass(false));//创建另外一个线程
        t1.start();//启动一个线程
        t2.start();//启动另外一个线程
    }
}
class DeadLockclass implements Runnable {
    public boolean falg;// 控制线程
    DeadLockclass(boolean falg) {
        this.falg = falg;
    }
    public void run() {
        /**
         * 若是falg的值为true则调用t1线程
         */
        if (falg) {
            while (true) {
                synchronized (Suo.o1) {
                    System.out.println("o1 " + Thread.currentThread().getName());
                    synchronized (Suo.o2) {
                        System.out.println("o2 " + Thread.currentThread().getName());
                    }
                }
            }
        }
        /**
         * 若是falg的值为false则调用t2线程
         */
        else {
            while (true) {
                synchronized (Suo.o2) {
                    System.out.println("o2 " + Thread.currentThread().getName());
                    synchronized (Suo.o1) {
                        System.out.println("o1 " + Thread.currentThread().getName());
                    }
                }
            }
        }
    }
}

class Suo {
    static Object o1 = new Object();
    static Object o2 = new Object();
}

当我启动该程序时,咱们看一下控制台:

咱们发现,程序只输出了两行内容,而后程序就再也不打印其它的东西了,可是程序并无中止。这样就产生了死锁。 当线程1使用synchronized锁住了o1的同时,线程2也是用synchronized锁住了o2。当两个线程都执行完第一个打印任务的时候,线程1想锁住o2,线程2想锁住o1。可是,线程1当前锁着o1,线程2锁着o2。因此两个想成都没法继续执行下去,就形成了死锁。

而后,咱们使用jstack来看一下线程堆栈信息

哈哈,堆栈写的很明显,它告诉咱们 Found one Java-level deadlock,而后指出形成死锁的两个线程的内容。而后,又经过 Java stack information for the threads listed above来显示更详细的死锁的信息。 

三、Jmap

什么是堆Dump

堆Dump是反应Java堆使用状况的内存镜像,其中主要包括系统信息虚拟机属性完整的线程Dump全部类和对象的状态等。 通常,在内存不足、GC异常等状况下,咱们就会怀疑有内存泄露。这个时候咱们就能够制做堆Dump来查看具体状况。分析缘由。

基础知识

常见内存错误:

outOfMemoryError 年老代内存不足。
outOfMemoryError:PermGen Space 永久代内存不足。
outOfMemoryError:GC overhead limit exceed 垃圾回收时间占用系统运行时间的98%或以上。

jmap用法

C:\Users\dup>jmap -help
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
    -clstats             to print class loader 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

参数:

option 选项参数是互斥的(不可同时使用)。想要使用选项参数,直接跟在命令名称后便可。
pid 须要打印配置信息的进程ID。该进程必须是一个Java进程。想要获取运行的Java进程列表,你可使用jps。
executable 产生核心dump的Java可执行文件。
core 须要打印配置信息的核心文件。
remote-hostname-or-IP 远程调试服务器的(请查看jsadebugd)主机名或IP地址。
server-id 可选的惟一id,若是相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。

选项:

<no option> 若是使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每一个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较类似。
-dump:[live,]format=b,file=<filename> 以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。若是指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可使用jhat(Java堆分析工具)读取生成的文件。
-finalizerinfo 打印等待终结的对象信息。
-heap 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
-histo[:live] 打印堆的柱状图。其中包括每一个Java类、对象数量、内存大小(单位:字节)、彻底限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。若是指定了live子选项,则只计算活动的对象。
-permstat 打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每一个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
-F 强制模式。若是指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
-h 打印帮助信息。
-help 打印帮助信息。
-J<flag> 指定传递给运行jmap的JVM的参数。

 查看java 堆(heap)使用状况,执行命令:jmap -heap 4644

Attaching to process ID 31846, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.71-b01

using thread-local object allocation.
Parallel GC with 4 thread(s)//GC 方式

Heap Configuration: //堆内存初始化配置
   MinHeapFreeRatio = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
   MaxHeapFreeRatio = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
   MaxHeapSize      = 2082471936 (1986.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize          = 1310720 (1.25MB)//对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize       = 17592186044415 MB//对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize          = 5439488 (5.1875MB)//对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
   NewRatio         = 2 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio    = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值 
   PermSize         = 21757952 (20.75MB)  //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
   MaxPermSize      = 85983232 (82.0MB)//对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage://堆内存使用状况
PS Young Generation
Eden Space://Eden区内存分布
   capacity = 33030144 (31.5MB)//Eden区总容量
   used     = 1524040 (1.4534378051757812MB)  //Eden区已使用
   free     = 31506104 (30.04656219482422MB)  //Eden区剩余容量
   4.614088270399305% used //Eden区使用比率
From Space:  //其中一个Survivor区的内存分布
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
To Space:  //另外一个Survivor区的内存分布
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
PS Old Generation //当前的Old区内存分布
   capacity = 86507520 (82.5MB)
   used     = 0 (0.0MB)
   free     = 86507520 (82.5MB)
   0.0% used
PS Perm Generation//当前的 “永生代” 内存分布
   capacity = 22020096 (21.0MB)
   used     = 2496528 (2.3808746337890625MB)
   free     = 19523568 (18.619125366210938MB)
   11.337498256138392% used

670 interned Strings occupying 43720 bytes.

查看堆内存(histogram)中的对象数量及大小。执行命令:jmap -histo 4464

num     #instances         #bytes  class name
编号     个数                字节     类名
----------------------------------------------
   1:             7        1322080  [I
   2:          5603         722368  <methodKlass>
   3:          5603         641944  <constMethodKlass>
   4:         34022         544352  java.lang.Integer
   5:           371         437208  <constantPoolKlass>
   6:           336         270624  <constantPoolCacheKlass>
   7:           371         253816  <instanceKlassKlass>

jmap -histo:live 这个命令执行,JVM会先触发gc,而后再统计信息。

将内存使用的详细状况输出到文件,jmap -dump:format=b,file=heapDump 4644

而后用jhat命令能够参看 jhat -port 5000 heapDump 在浏览器中访问:http://localhost:5000/ 查看详细信息

C:\Users\dup>jmap -dump:format=b,file=heapDump 4644
Dumping heap to C:\Users\dup\heapDump ...
Heap dump file created

C:\Users\dup>jhat port 5000 heapDump
Reading from heapDump...
Dump file created Sun Oct 14 09:19:16 CST 2018
Snapshot read, resolving...
Resolving 21860 objects...
Chasing references, expect 4 dots....
Eliminating duplicate references....
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

 

这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap若是比较大的话,就会致使这个过程比较耗时,而且执行的过程当中为了保证dump的信息是可靠的,因此会暂停应用。

总结

1.若是程序内存不足或者频繁GC,颇有可能存在内存泄露状况,这时候就要借助Java堆Dump查看对象的状况。
2.要制做堆Dump能够直接使用jvm自带的jmap命令
3.能够先使用jmap -heap命令查看堆的使用状况,看一下各个堆空间的占用状况。
4.使用jmap -histo:[live]查看堆内存中的对象的状况。若是有大量对象在持续被引用,并无被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
5.也可使用 jmap -dump:format=b,file=<fileName>命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几回内存,把内存文件进行编号归档,便于后续内存整理分析。

四、 jstat 

jstat(JVM Statistics Monitoring Tool)是用于监控虚拟机各类运行状态信息的命令行工具。他能够显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形的服务器上,它是运行期定位虚拟机性能问题的首选工具。

jstat 命令格式

参数解释:

Option — 选项,咱们通常使用 -gcutil 查看gc状况

vmid — VM的进程号,即当前运行的java进程号

interval– 间隔时间,单位为秒或者毫秒

count — 打印次数,若是缺省则打印无数次

参数interval和count表明查询间隔和次数,若是省略这两个参数,说明只查询一次。假设须要每250毫秒查询一次进程5828垃圾收集情况,一共查询5次,那命令行以下:

C:\Users\dup>jstat -gc  4644 250 5
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU
   CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000

对于命令格式中的VMIDLVMID须要特别说明下:若是是本地虚拟机进程,VMID(Virtual Machine IDentifier,虚机标识符)和LVMID(Local Virtual Machine IDentifier,虚机标识符)是一致的,若是是远程虚拟机进程,那VMID的格式应当是:[protocol:][//] lvmid [@hostname[:port]/servername]

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

常见术语

一、jstat –class<pid> : 显示加载class的数量,及所占空间等信息。

C:\Users\dup>jstat -class 4644
Loaded  Bytes  Unloaded  Bytes     Time
   418   452.1        0     0.0       0.10

Loaded 装载的类的数量 Bytes 装载类所占用的字节数 Unloaded 卸载类的数量 Bytes 卸载类的字节数 Time 装载和卸载类所花费的时间

二、jstat -compiler <pid>显示VM实时编译的数量等信息。

C:\Users\dup>jstat -compiler 4644
Compiled Failed Invalid   Time   FailedType FailedMethod
      24      0       0     0.01          0

Compiled 编译任务执行数量 Failed 编译任务执行失败数量 Invalid 编译任务执行失效数量Time 编译任务消耗时间 FailedType 最后一个编译失败任务的类型 FailedMethod 最后一个编译失败任务所在的类及方法

三、jstat -gc <pid>: 能够显示gc的信息,查看gc的次数,及时间。

C:\Users\dup>jstat -gc 4644
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU
   CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000

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)

四、jstat -gccapacity <pid>:能够显示,VM内存中三代(young,old,perm)对象的使用和占用大小

C:\Users\dup>jstat -gccapacity 4644
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC
       OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC

  5440.0  87360.0   5440.0  512.0  512.0   4416.0    10944.0   174784.0    10944
.0    10944.0      0.0   4480.0   2240.0      0.0      0.0      0.0      0     0

NGCMN 年轻代(young)中初始化(最小)的大小(字节) NGCMX 年轻代(young)的最大容量 (字节)NGC 年轻代(young)中当前的容量 (字节) S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节) EC 年轻代中Eden(伊甸园)的容量 (字节) OGCMN old代中初始化(最小)的大小 (字节) OGCMX old代的最大容量(字节) OGC old代当前新生成的容量 (字节) OC Old代的容量 (字节) PGCMN perm代中初始化(最小)的大小 (字节)PGCMX perm代的最大容量 (字节)
PGC perm代当前新生成的容量 (字节) PC Perm(持久代)的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数

五、jstat -gcutil <pid>:统计gc信息

C:\Users\dup>jstat -gcutil 4644
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT

  0.00   0.00  39.90   0.00  23.03      -      0    0.000     0    0.000    0.00
0

S0 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 S1 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 E 年轻代中Eden(伊甸园)已使用的占当前容量百分比 O old代已使用的占当前容量百分比 P perm代已使用的占当前容量百分比 YGC 从应用程序启动到采样时年轻代中gc次数 YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

六、jstat -gcnew <pid>:年轻代对象的信息。

C:\Users\dup>jstat -gcnew  4644
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
 512.0  512.0    0.0    0.0 15  15    0.0   4416.0   1762.2      0    0.000

S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节) S0U 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) S1U 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) TT 持有次数限制 MTT 最大持有次数限制EC 年轻代中Eden(伊甸园)的容量 (字节) EU 年轻代中Eden(伊甸园)目前已使用空间 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)

七、jstat -gcnewcapacity<pid>: 年轻代对象的信息及其占用量。

C:\Users\dup>jstat -gcnewcapacity 4644
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX
    EC      YGC   FGC
    5440.0    87360.0     5440.0   8704.0    512.0   8704.0    512.0    69952.0
    4416.0     0     0

NGCMN 年轻代(young)中初始化(最小)的大小(字节) NGCMX 年轻代(young)的最大容量 (字节)NGC 年轻代(young)中当前的容量 (字节) S0CMX 年轻代中第一个survivor(幸存区)的最大容量 (字节) S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1CMX 年轻代中第二个survivor(幸存区)的最大容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节)ECMX 年轻代中Eden(伊甸园)的最大容量 (字节) EC 年轻代中Eden(伊甸园)的容量 (字节)YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数

八、jstat -gcold <pid>:old代对象的信息。

C:\Users\dup>jstat -gcold 4644
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT
    GCT
  2240.0    515.9      0.0      0.0     10944.0         0.0      0     0    0.00
0    0.000

PC Perm(持久代)的容量 (字节) PU Perm(持久代)目前已使用空间 (字节) OC Old代的容量 (字节) OU Old代目前已使用空间 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

九、jstat-gcoldcapacity <pid>:old代对象的信息及其占用量。

C:\Users\dup>jstat -gcoldcapacity 4644
   OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT
    10944.0    174784.0     10944.0     10944.0     0     0    0.000    0.000

OGCMN old代中初始化(最小)的大小 (字节) OGCMX old代的最大容量(字节) OGC old代当前新生成的容量 (字节) OC Old代的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

十、jstat -gcpermcapacity<pid>: perm对象的信息及其占用量。

PGCMN perm代中初始化(最小)的大小 (字节) PGCMX perm代的最大容量 (字节)
PGC perm代当前新生成的容量 (字节) PC Perm(持久代)的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

十一、jstat -printcompilation <pid>:当前VM执行的信息。

C:\Users\dup>jstat -printcompilation 4644
Compiled  Size  Type Method
      24     26    1 java/util/ArrayList ensureExplicitCapacity

Compiled 编译任务的数目 Size 方法生成的字节码的大小 Type 编译类型 Method 类名和方法名用来标识编译的方法。类名使用/作为一个命名空间分隔符。方法名是给定类中的方法。上述格式是由-XX:+PrintComplation选项进行设置的

五、jinfo

jinfo能够输出java进程、core文件或远程debug服务器的配置信息。这些配置信息包括JAVA系统参数及命令行参数,若是进程运行在64位虚拟机上,须要指明-J-d64参数,如:jinfo -J-d64 -sysprops pid

六、jhat

hat(Java Heap Analysis Tool),是一个用来分析java的堆状况的命令。以前的文章讲到过,使用jmap能够生成Java堆的Dump文件。生成dump文件以后就能够用jhat命令,将dump文件转成html的形式,而后经过http访问能够查看堆状况。jhat命令会Java解析堆dump并启动一个web服务器,而后就能够在浏览器中查看堆的dump文件了。

1、导出dump文件

关于dump文件的生成能够看jmap命令的详细介绍.

一、运行java程序

package com.test;

public class JhatDemo {

    public static void main(String[] args) {
        while(true) {
            String string = new String("hollis");
            System.out.println(string);
        }
    }
}

二、查看该进程的ID,生成dump文件

C:\Users\dup>jmap -dump:format=b,file=headDump 1064
Dumping heap to C:\Users\dup\headDump ...
Heap dump file created

除了使用jmap命令,还能够经过如下方式:

一、使用 jconsole 选项经过 HotSpotDiagnosticMXBean 从运行时得到堆转储(生成dump文件)、

二、虚拟机启动时若是指定了 -XX:+HeapDumpOnOutOfMemoryError 选项, 则在抛出 OutOfMemoryError 时, 会自动执行堆转储。

三、使用 hprof 命令

2、解析Java堆转储文件,并启动一个 web server

C:\Users\dup>jhat headDump
Reading from headDump...
Dump file created Sun Oct 14 10:04:16 CST 2018
Snapshot read, resolving...
Resolving 85722 objects...
Chasing references, expect 17 dots.................
Eliminating duplicate references.................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

使用jhat命令,就启动了一个http服务,端口是7000

而后在访问http://localhost:7000/

页面以下:

3、分析

在浏览器里面看到dump文件以后就能够进行分析了。这个页面会列出当前进程中的全部对像状况。

该页面提供了几个查询功能可供使用:

All classes including platform
Show all members of the rootset
Show instance counts for all classes (including platform)
Show instance counts for all classes (excluding platform)
Show heap histogram
Show finalizer summary
Execute Object Query Language (OQL) query

通常查看堆异常状况主要看这个两个部分:

Show instance counts for all classes (excluding platform),平台外的全部对象信息。以下图:

Show heap histogram 以树状图形式展现堆状况。以下图:

具体排查时须要结合代码,观察是否大量应该被回收的对象在一直被引用或者是否有占用内存特别大的对象没法被回收。

用法摘要

C:\Users\dup>jhat -help
Usage:  jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-
debug <int>] [-version] [-h|-help] <file>

        -J<flag>          Pass <flag> directly to the runtime system. For
                          example, -J-mx512m to use a maximum heap size of 512MB

        -stack false:     Turn off tracking object allocation call stack.
        -refs false:      Turn off tracking of references to objects
        -port <port>:     Set the port for the HTTP server.  Defaults to 7000
        -exclude <file>:  Specify a file that lists data members that should
                          be excluded from the reachableFrom query.
        -baseline <file>: Specify a baseline object dump.  Objects in
                          both heap dumps with the same ID and same class will
                          be marked as not being "new".
        -debug <int>:     Set debug level.
                            0:  No debug output
                            1:  Debug hprof file parsing
                            2:  Debug hprof file parsing, no server
        -version          Report version number
        -h|-help          Print this help and exit
        <file>            The file to read

For a dump file that contains multiple heap dumps,
you may specify which dump in the file
by appending "#<number>" to the file name, i.e. "foo.hprof#3".

All boolean options default to "true"

-stack false|true

关闭对象分配调用栈跟踪(tracking object allocation call stack)。 若是分配位置信息在堆转储中不可用. 则必须将此标志设置为 false. 默认值为 true.

-refs false|true

关闭对象引用跟踪(tracking of references to objects)。 默认值为 true. 默认状况下, 返回的指针是指向其余特定对象的对象,如反向连接或输入引用(referrers or incoming references), 会统计/计算堆中的全部对象。

-port port-number

设置 jhat HTTP server 的端口号. 默认值 7000.

-exclude exclude-file

指定对象查询时须要排除的数据成员列表文件(a file that lists data members that should be excluded from the reachable objects query)。 例如, 若是文件列列出了 java.lang.String.value , 那么当从某个特定对象 Object o 计算可达的对象列表时, 引用路径涉及 java.lang.String.value 的都会被排除。

-baseline exclude-file

指定一个基准堆转储(baseline heap dump)。 在两个 heap dumps 中有相同 object ID 的对象会被标记为不是新的(marked as not being new). 其余对象被标记为新的(new). 在比较两个不一样的堆转储时颇有用.

-debug int

设置 debug 级别. 0 表示不输出调试信息。 值越大则表示输出更详细的 debug 信息.

-version

启动后只显示版本信息就退出

-J< flag >

由于 jhat 命令实际上会启动一个JVM来执行, 经过 -J 能够在启动JVM时传入一些启动参数. 例如, -J-Xmx512m 则指定运行 jhat 的Java虚拟机使用的最大堆内存为 512 MB. 若是须要使用多个JVM启动参数,则传入多个 -Jxxxxxx.

相关文章
相关标签/搜索