先说说JVM(Java虚拟机)吧。话说是一个运行平台。在编译器编译产生的class文件会在JVM中被加载和解释执行的。JVM有许多种,早期的是SUN classic VM之后出现hotspot和BEA公司的JRockit、还有IBM的J9。他们都是JVM,但是我们现在接触的是Hotspot,他是SUN公司的,不过现在是Oracle公司的了。
1、 java的内存动态分配和垃圾回收机制,这两个特点是java区别c++的最大的原因。java的内存区域分成了几部分的数据区:方法区、堆、虚拟机栈、本地方法栈、程序计数器。
程序计数器:首先是线程隔离的。他可以看作是线程所执行的字节码行号的指示器。字节码解释器会根据计数器来选取将要执行的语句。注意他是线程隔离的,每条线程都有自己的程序计数器。对于单核 CPU 而言,每一时刻只能有一个线程在运行,而其他线程必须被切换出去。为此,每一个线程都必须用一个独立的程序计数器,用于记录下一条要运行的指令。各个线程之间的计数器互不影响,独立工作,是一块线程独有的内存空间。各个线程的计数器是互不影响的。这个区域是唯一没有任何规定会出现OutMemoryError的区域。
虚拟机栈:也是线程隔离的。就是我们平常经常说的栈。虚拟机栈用于存放函数调用堆栈信息。Java 虚拟机栈也是线程私有的内存空间,它和 Java 线程在同一时间创建,它保存方法的局部变量、部分结果,并参与方法的调用和返回。主要用来存储局部变量表、操作数栈、动态链接、方法出口等。局部变量表存放了编译期出现的基本数据类型、对象引用(在hotspot中对象的引用就是堆中对象实例的绝对地址的值)。
下面的例子展示了一个递归调用的应用。计数器 count 记录了递归的层次,这个没有出口的递归函数一定会导致栈溢出。程序则在栈溢出时,打印出栈的当前深度。
public class TestStack { private int count = 0; //没有出口的递归函数 public void recursion(){ count++;//每次调用深度加 1 recursion();//递归 } public void testStack(){ try{ recursion(); }catch(Throwable e){ System.out.println("deep of stack is "+count);//打印栈溢出的深度 e.printStackTrace(); } } public static void main(String[] args){ TestStack ts = new TestStack(); ts.testStack(); } }结果如下:
ava.lang.StackOverflowError
at TestStack.recursion(TestStack.java:7)
at TestStack.recursion(TestStack.java:7)
at TestStack.recursion(TestStack.java:7)
at TestStack.recursion(TestStack.java:7)
at TestStack.recursion(TestStack.java:7)
at TestStack.recursion(TestStack.java:7)
at TestStack.recursion(TestStack.java:7)deep of stack is 9013
虚拟机栈在运行时使用一种叫做栈帧的数据结构保存上下文数据。在栈帧中,存放了方法的局部变量表、操作数栈、动态连接方法和返回地址等信息。每一个方法的调用都伴随着栈帧的入栈操作。相应地,方法的返回则表示栈帧的出栈操作。如果方法调用时,方法的参数和局部变量相对较多,那么栈帧中的局部变量表就会比较大,栈帧会膨胀以满足方法调用所需传递的信息。因此,单个方法调用所需的栈空间大小也会比较多。
注意:函数嵌套调用的次数由栈的大小决定。栈越大,函数嵌套调用次数越多。对一个函数而言,它的参数越多,内部局部变量越多,它的栈帧就越大,其嵌套调用次数就会减少。
本地方法栈:是线程隔离的,主要是为了使用本地方法服务的。在hotspot中将本地方法栈和虚拟机栈是合二为一的。本地方法栈并不是用 Java 实现的,而是使用 C 实现的。在 SUN 的 HotSpot 虚拟机中,不区分本地方法栈和虚拟机栈。因此,和虚拟机栈一样,它也会抛出 StackOverflowError 和 OutofMemoryError。
堆:首先是线程共享的。主要就是存放对象实例的。同时堆也是垃圾收集机制管理的主要区域。堆用于存放 Java 程序运行时所需的对象等数据。几乎所有的对象和数组都是在堆中分配空间的。Java 堆分为新生代和老生代两个部分,新生代用于存放刚刚产生的对象和年轻的对象,如果对象一直没有被回收,生存得足够长,老年对象就被移入老年代。新生代又可进一步细分为 eden、survivor space0 和 survivor space1。eden 即对象的出生地,大部分对象刚刚建立时都会被存放在这里。survivor 空间是存放其中的对象至少经历了一次垃圾回收,并得以幸存下来的。如果在幸存区的对象到了指定年龄仍未被回收,则有机会进入老年代 (tenured)。下面例子演示了对象在内存中的分配方式。
代码如下:
public class TestHeapGC { public static void main(String[] args){ byte[] b1 = new byte[1024*1024/2]; byte[] b2 = new byte[1024*1024*8]; b2 = null; b2 = new byte[1024*1024*8];//进行一次新生代 GC System.gc(); } }
VM的参数设置如下:
-XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -Xms40M -Xmx40M -Xmn20M
结果输出如下:
[GC [DefNew: 9031K->661K(18432K), 0.0022784 secs] 9031K->661K(38912K), 0.0023178 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] Heap def new generation total 18432K, used 9508K [0x34810000, 0x35c10000, 0x35c10000) eden space 16384K, 54% used [0x34810000, 0x350b3e58, 0x35810000) from space 2048K, 32% used [0x35a10000, 0x35ab5490, 0x35c10000) to space 2048K, 0% used [0x35810000, 0x35810000, 0x35a10000) tenured generation total 20480K, used 0K [0x35c10000, 0x37010000, 0x37010000) the space 20480K, 0% used [0x35c10000, 0x35c10000, 0x35c10200, 0x37010000) compacting perm gen total 12288K, used 374K [0x37010000, 0x37c10000, 0x3b010000) the space 12288K, 3% used [0x37010000, 0x3706db10, 0x3706dc00, 0x37c10000) ro space 10240K, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000) rw space 12288K, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
以上是GC日志。理解它是很重要的。我来写一个注释版本的吧。
[GC (表示垃圾回收)[DefNew: 9031K->661K(18432K)(新生代的堆大小是:18432K,其中没有gc之前使用了9031K,gc后使用了661K), 0.0022784 secs(gc的用时)] 9031K->661K(38912K),(堆的总大小是:38912K,其中没有gc之前使用了9031K,gc后使用了661K) 0.0023178 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] Heap(堆的具体信息,分为新生代和老年代。新生代又分为Eden区、from survivor区和to survivor区。大小比例是8:1:1)。 def new generation total 18432K, used 9508K [0x34810000, 0x35c10000, 0x35c10000)(新生代的大小和使用率) eden space 16384K, 54% used [0x34810000, 0x350b3e58, 0x35810000)(eden区的大小和使用率) from space 2048K, 32% used [0x35a10000, 0x35ab5490, 0x35c10000)(from survivor的大小和使用率) to space 2048K, 0% used [0x35810000, 0x35810000, 0x35a10000)(to survivor的大小和使用率) tenured generation total 20480K, used 0K [0x35c10000, 0x37010000, 0x37010000)(老年代的大小和使用率) the space 20480K, 0% used [0x35c10000, 0x35c10000, 0x35c10200, 0x37010000) compacting perm gen total 12288K, used 374K [0x37010000, 0x37c10000, 0x3b010000) the space 12288K, 3% used [0x37010000, 0x3706db10, 0x3706dc00, 0x37c10000) ro space 10240K, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000) rw space 12288K, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
上述输出显示 JVM 在进行多次内存分配的过程中,触发了一次新生代 GC。在这次 GC 中,原本分配在 eden 段的变量 b1 被移动到 from 空间段 (s0)。最后分配的 8MB 内存被分配在 eden 新生代。
方法区:是线程共享的。在hotspot虚拟机中方法区也属于堆的一部分。方法区用于存放程序的类元数据信息。方法区与堆空间类似,它也是被 JVM 中所有的线程共享的。方法区主要保存的信息是类的元数据。方法区中最为重要的是类的类型信息、常量池、域信息、方法信息。类型信息包括类的完整名称、父类的完整名称、类型修饰符和类型的直接接口类表;常量池包括这个类方法、域等信息所引用的常量信息;域信息包括域名称、域类型和域修饰符;方法信息包括方法名称、返回类型、方法参数、方法修饰符、方法字节码、操作数栈和方法栈帧的局部变量区大小以及异常表。总之,方法区内保持的信息大部分来自于 class 文件,是 Java 应用程序运行必不可少的重要数据。
在 Hot Spot 虚拟机中,方法区也称为永久区,是一块独立于 Java 堆的内存空间。虽然叫做永久区,但是在永久区中的对象同样也可以被 GC 回收的。只是对于 GC 的表现也和 Java 堆空间略有不同。对永久区 GC 的回收,通常主要从两个方面分析:一是 GC 对永久区常量池的回收;二是永久区对类元数据的回收。Hot Spot 虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收。
下面代码会生成大量 String 对象,并将其加入常量池中。String.intern() 方法的含义是如果常量池中已经存在当前 String,则返回池中的对象,如果常量池中不存在当前 String 对象,则先将 String 加入常量池,并返回池中的对象引用。因此,不停地将 String 对象加入常量池会导致永久区饱和。如果 GC 不能回收永久区的这些常量数据,那么就会抛出 OutofMemoryError 错误。
package www.wq.ch02; public class permGenGC { public static void main(String[] args){ for(int i=0;i<Integer.MAX_VALUE;i++){ String t = String.valueOf(i).intern();//加入常量池 } } }
结果显示: [GC (Allocation Failure) [DefNew: 4416K->512K(4928K), 0.0143925 secs] 4416K->4029K(15872K), 0.0144659 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 4928K->511K(4928K), 0.0204818 secs] 8445K->8443K(15872K), 0.0205315 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] [GC (Allocation Failure) [DefNew: 4927K->511K(4928K), 0.0216787 secs][Tenured: 12346K->1245K(12352K), 0.0139809 secs] 12859K->1245K(17280K), [Metaspace: 84K->84K(4480K)], 0.0358838 secs] [Times: user=0.03 sys=0.00, real=0.04 secs] [GC (Allocation Failure) [DefNew: 4480K->512K(4992K), 0.0217371 secs] 5725K->5635K(15936K), 0.0217855 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] [GC (Allocation Failure) [DefNew: 4992K->512K(4992K), 0.0372645 secs] 10115K->10114K(15936K), 0.0373091 secs] [Times: user=0.05 sys=0.00, real=0.04 secs] [GC (Allocation Failure) [DefNew: 4992K->511K(4992K), 0.0447900 secs][Tenured: 14081K->1245K(14144K), 0.0386709 secs] 14594K->1245K(19136K), [Metaspace: 84K->84K(4480K)], 0.0838196 secs] [Times: user=0.09 sys=0.00, real=0.08 secs] [GC (Allocation Failure) [DefNew: 4480K->511K(4992K), 0.0246358 secs] 5725K->5679K(15936K), 0.0246804 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] [GC (Allocation Failure) [DefNew: 4991K->512K(4992K), 0.0334968 secs] 10159K->10157K(15936K), 0.0335449 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] [GC (Allocation Failure) [DefNew: 4992K->511K(4992K), 0.0471104 secs][Tenured: 14124K->1245K(14144K), 0.0474985 secs] 14637K->1245K(19136K), [Metaspace: 84K->84K(4480K)], 0.0949881 secs] [Times: user=0.09 sys=0.00, real=0.10 secs] [GC (Allocation Failure) [DefNew: 4480K->512K(4992K), 0.0213226 secs] 5725K->5724K(15936K), 0.0213714 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
每当常量池饱和时,FULL GC 总能顺利回收常量池数据,确保程序稳定持续进行。
2、Hotspot虚拟机对象结构
当虚拟机遇到new指令的时候,首先检查能不能在常量池中找到该类的符号引用。顺便检查是否已被加载、解析和初始化过。如果没有就进行相应的类加载。接下来虚拟机为新对象分配内存。一般会由两种分配方式:指针碰撞和空闲列表。通常采用的是空闲列表法。
对象在内存中的布局分为3块区域:对象头、实例数据和对齐填充。
对象头:包含两部分。一是存储对象自身运行时的数据。另一部分是类型指针即对象指向它的类元数据的指针。虚拟机通过这个指针来确定这个对象是那个类的实例。
实例数据:代码中所定义的各种类型的字段。无论是从父类继承的,还是子类定义的都需要记录下来。
对齐填充:仅仅是起占位符的作用。由于hotspot VM要求对象的起始地址必须是8字节的整数倍。因此当对象实例数据没有对齐时就需要通过对齐填充来补全。
这一点很重要是java中不存在引用传递,只有值传递。在java程序中通过栈上的reference数据来操作堆上的具体对象。reference是一个指向对象的引用。hotspot在对象的访问方式上使用的是直接地址。即reference数据存储的就是堆中对象的绝对地址的值。
目前主流的访问方式是:直接地址法和句柄访问法。hotspot采用的是直接地址法。
4、JVM的堆参数调优:
Java 堆参数总结,Java 应用程序可以使用的最大堆可以用-Xmx 参数指定。最大堆指的是新生代和老生代的大小之和的最大值,它是 Java 应用程序的堆上限。
Java 堆操作是主要的数据存储操作,总结的主要参数配置如下。
与 Java 应用程序堆内存相关的 JVM 参数有:
-Xms:设置 Java 应用程序启动时的初始堆大小;
-Xmx:设置 Java 应用程序能获得的最大堆大小;
-Xss:设置线程栈的大小;
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例。当堆空间的空闲内存小于这个数值时,JVM 便会扩展堆空间;
-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例。当堆空间的空闲内存大于这个数值时,便会压缩堆空间,得到一个较小的堆;
-XX:NewSize:设置新生代的大小;
-XX:NewRatio:设置老年代与新生代的比例,它等于老年代大小除以新生代大小;
-XX:SurvivorRatio:新生代中 eden 区与 survivor 区的比例;
-XX:MaxPermSize:设置最大的持久区大小;
-XX:TargetSurvivorRatio: 设置 survivor 区的可使用率。当 survivor 区的空间使用率达到这个数值时,会将对象送入老年代。
现在说说java的各种工具。包括jps, jstack, jmap, jconsole, jinfo, jhat, javap等。这张图是在jdk的bin文件目录下的:
下面是命令行监控的主要工具和用途:
名称 |
作用 |
jps |
JVM Process Status Tool,现实指定系统内所有的HotSpot虚拟机进程 |
jstat |
JVM Statistics Monitoring Tool,用于收集Hotspot虚拟机各个方面的运行参数 |
jinfo |
Configuration Info for Java,现实虚拟机配置信息 |
jmap |
Memory map for java,生成虚拟机的内存转储快照 |
jhat |
JVM heap Dunp Browser,用于分析heapdump文件,他会建立一个HTTP/HTML服务,让用户可通过浏览器查看 |
jstack |
Stack Track for java ,显示虚拟机线程快照 |
一、非可视化的工具
1、jps
jps用来查看基于HotSpot的JVM里面中,所有具有访问权限的Java进程的具体状态, 包括进程ID,进程启动的路径及启动参数等等,与unix上的ps类似,只不过jps是用来显示java进程,可以把jps理解为ps的一个子集。 使用jps时,如果没有指定hostid,它只会显示本地环境中所有的Java进程;如果指定了hostid,它就会显示指定hostid上面的java进程,不过这需要远程服务上开启了jstatd服务,可以参看前面的jstatd章节来启动jstad服务。
命令格式 :jps [ options ] [ hostid ]
参数说明 :
-q 忽略输出的类名、Jar名以及传递给main方法的参数,只输出pid。
-m 输出传递给main方法的参数,如果是内嵌的JVM则输出为null。
-l 输出应用程序主类的完整包名,或者是应用程序JAR文件的完整路径。
-v 输出传给JVM的参数。
-V 输出通过标记的文件传递给JVM的参数(.hotspotrc文件,或者是通过参数-XX:Flags=<filename>指定的文件)。
-J 用于传递jvm选项到由javac调用的java加载器中,例如,“-J-Xms48m”将把启动内存设置为48M,使用-J选项可以非常方便的向基于Java的开发的底层虚拟机应用程序传递参数。下面样例均在linux的jdk1.7下测试。
使用样例:
C:\Users\Administrator>jps 78144 102612 Jps C:\Users\Administrator>jps -l 78144 102892 sun.tools.jps.Jps C:\Users\Administrator>jps -q 78144 102760 C:\Users\Administrator>jps -v 78144 -Dosgi.requiredJavaVersion=1.6 -Xms40m -Xmx512m 102904 Jps -Denv.class.path=.;C:\mysql-connector-java-5.0.8\mysql-connector-java -5.0.8-bin.jar; -Dapplication.home=D:\Java\jdk1.8.0_25 -Xms8m
2、jstat
Jstat用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计,使用jstat我们可以对指定的JVM做如下监控:
- 类的加载及卸载情况
- 查看新生代、老生代及持久代的容量及使用情况
- 查看新生代、老生代及持久代的垃圾收集情况,包括垃圾回收的次数及垃圾回收所占用的时间
- 查看新生代中Eden区及Survior区中容量及分配情况等
jstat工具特别强大,它有众多的可选项,通过提供多种不同的监控维度,使我们可以从不同的维度来了解到当前JVM堆的使用情况。详细查看堆内各个部分的使用量,使用的时候必须加上待统计的Java进程号,可选的不同维度参数以及可选的统计频率参数。
命令格式:
jstat [ option vmid [interval][s|ms][count]]
option 参数如下面表格
Option Displays...
|
|
class |
用于查看类加载情况的统计 |
compiler |
用于查看HotSpot中即时编译器编译情况的统计 |
gc |
用于查看JVM中堆的垃圾收集情况的统计 |
gccapacity |
用于查看新生代、老生代及持久代的存储容量情况 |
gccause |
用于查看垃圾收集的统计情况(这个和-gcutil选项一样),如果有发生垃圾收集,它还会显示最后一次及当前正在发生垃圾收集的原因。 |
gcnew |
用于查看新生代垃圾收集的情况 |
gcnewcapacity |
用于查看新生代的存储容量情况 |
gcold |
用于查看老生代及持久代发生GC的情况 |
gcoldcapacity |
用于查看老生代的容量 |
gcpermcapacity |
用于查看持久代的容量 |
gcutil |
用于查看新生代、老生代及持代垃圾收集的情况 |
printcompilation |
HotSpot编译方法的统计 |
interval 和count 代表查询次数和间隔。
C:\Users\Administrator>jstat -class 105244 Loaded Bytes Unloaded Bytes Time 440 475.5 0 0.0 0.33 C:\Users\Administrator>jstat -compiler 105244 Compiled Failed Invalid Time FailedType FailedMethod 19 0 0 0.01 0 C:\Users\Administrator>jstat -gc 105244 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 512.0 512.0 512.0 0.0 4480.0 3225.7 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 152 4.435 50 1.539 5.974
C:\Users\Administrator>jstat -gc 105244 100 10 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 512.0 512.0 0.0 512.0 4480.0 3763.3 10944.0 4572.6 2368.0 1757. 0 0.0 0.0 157 4.579 52 1.602 6.181 512.0 512.0 0.0 512.0 4480.0 3852.9 10944.0 4572.6 2368.0 1757. 0 0.0 0.0 157 4.579 52 1.602 6.181 512.0 512.0 0.0 512.0 4480.0 4211.3 10944.0 4572.6 2368.0 1757. 0 0.0 0.0 157 4.579 52 1.602 6.181 512.0 512.0 0.0 512.0 4480.0 4480.0 10944.0 4572.6 2368.0 1757. 0 0.0 0.0 158 4.579 52 1.602 6.181 512.0 512.0 512.0 0.0 4480.0 179.2 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 158 4.611 52 1.602 6.213 512.0 512.0 512.0 0.0 4480.0 448.0 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 158 4.611 52 1.602 6.213 512.0 512.0 512.0 0.0 4480.0 627.2 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 158 4.611 52 1.602 6.213 512.0 512.0 512.0 0.0 4480.0 896.0 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 158 4.611 52 1.602 6.213 512.0 512.0 512.0 0.0 4480.0 1254.5 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 158 4.611 52 1.602 6.213 512.0 512.0 512.0 0.0 4480.0 1523.3 10944.0 9051.4 2368.0 1757. 0 0.0 0.0 158 4.611 52 1.602 6.213
表示查询系统进程为105244的java程序gc,每100毫秒查询一次,一共查询十次,显示结果每列的含义如下:
S0C |
新生代中Survivor space中S0当前容量的大小(KB) |
S1C |
新生代中Survivor space中S1当前容量的大小(KB) |
S0U |
新生代中Survivor space中S0容量使用的大小(KB) |
S1U |
新生代中Survivor space中S1容量使用的大小(KB) |
EC |
Eden space当前容量的大小(KB) |
EU |
Eden space容量使用的大小(KB) |
OC |
Old space当前容量的大小(KB) |
OU |
Old space使用容量的大小(KB) |
PC |
Permanent space当前容量的大小(KB) |
PU |
Permanent space使用容量的大小(KB) |
YGC |
从应用程序启动到采样时发生 Young GC 的次数 |
YGCT |
从应用程序启动到采样时 Young GC 所用的时间(秒) |
FGC |
从应用程序启动到采样时发生 Full GC 的次数 |
FGCT |
从应用程序启动到采样时 Full GC 所用的时间(秒) |
GCT |
T从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC |
其他以gc开始的结果列跟gc选项结果列基本一样,这里不一一列举了。
3、jinfo
jinfo可以输出并修改运行时的java 进程的opts。用处比较简单,用于输出JAVA系统参数及命令行参数。
命令格式:
jinfo [option] pid
使用样例:
C:\Users\Administrator>jinfo -flag MaxNewSize 105244 -XX:MaxNewSize=89456640
二、JDK可视化工具(重点)
1、jconsole
JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。如果要把一个应用以可管理的形式启动,可以在启动是设置com.sun.management.jmxremote。
jconsole可以选择本地连接,来查看本地java程序参数,也可以连接远程机器来使用,下面连接本地看:
现在我的测试代码permGenGC.java的代码如下:
package www.wq.ch02;
public class permGenGC {
public static void main(String[] args){
for(int i=0;i<Integer.MAX_VALUE;i++){
String t = String.valueOf(i).intern();//加入常量池
}
}
}
运行结果如下:
现在通过JConsole来观察:
堆内存使用图:
线程图:这里我只有一个main线程
类;
JVM的摘要:
2、jvisualVM
jvisualVM所谓多合一虚拟机故障处理工具,有强大的插件扩展功能,通过安装插件扩展支持,jvisualVM可以做到:
a、显示虚拟机进程及进程的配置和环境信息(jps,jinfo);
b、监视应用程序CPU、GC、堆、方法区及线程的信息(jstat、jstack);
c、dump及分析堆转储快照(jmap、jhat);
d、方法级的程序性能分析,找出调用最多,运行时间最长的方法;
.....其它通过插件可以做到的;
概述里面可以看到虚拟机版本及配置的参数等。