JVM知识点整理

一、JVM是什么

JVM就是Java虚拟机(Java Virtual Machine),简单来说,JVM消除了开发人员在开发过程中对操作系统差异的感知,一段Java代码,能在Linux的JVM上跑,也能在Windows的JVM上跑。 Java 虚拟机就是一个字节码翻译器,它将字节码文件(也就是java代码编译后产生的.class文件)翻译成各个系统对应的机器码,确保字节码文件能在各个系统正确运行。

二、JAVA类加载机制

JVM是如何读取class文件并把类加载到内存里的?

1.类加载器

类加载器:BootstrapClassLoader、ExtClassLoader、AppClassLoader结构层次如下:

类加载器

注意:这里父类加载器不是通过继承实现的,而是采用组合实现的。

启动类加载器BootstrapClassLoader

负责加载存放在JDK/jre/lib目录下,或者被-Xbootclasspath参数指定的路径中,能被虚拟机识别的类库(例如rt.jar,所有包名是java.开头的都是由BootstrapClassLoader加载)。启动类加载器无法被Java程序直接引用到。

扩展类加载器ExtClassLoader

由sun.misc.Launcher$ExtClassLoader实现,负责加载JDK\jre\lib\ext目录下,或者系统变量java.ext.dirs指定路径中的所有类库(例如javax.开头的),开发者可以直接使用扩展类加载器。

应用程序类加载器AppClassLoader

由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径(ClassPath),如果没有自定义过类加载器,一般情况下,这个就是默认的类加载器。

2.JVM类加载机制

全盘负责:当一个类加载器负责加载某个Class时,该Class所有依赖的和引用的其他Class也由这个类加载器加载,除非显式使用另外一个类加载器来载入。

父类委托:先让父加载器尝试加载,只有父类无法加载这个类的时候才会尝试从自己的类路径中加载该类。

缓存机制:保证所有加载过的Class都会被缓存,程序需要某个Class的时候,类加载器会先从缓存区寻找,找不到才会读取该类对应的二进制文件,转换成Class对象,存入缓存。所以修改了Class文件后,需要重启JVM修改才会生效。

3.类的生命周期


类的生命周期

①加载:通过一个类的全限定名来获取其定义的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,在Java堆中生成一个代表这个类的 java.lang.Class对象,作为对方法区中这些数据的访问入口。

②连接:

    - 验证:文件格式验证,元数据验证,字节码验证,符号引用验证验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用 -Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

    - 准备:为类的静态变量分配内存,并将其初始化为默认值。这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。

    - 解析:把类中的符号引用转换为直接引用。

    - 初始化:为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:1.创建类的实例,也就是new的方式;访问某个类或接口的静态变量,或者对该静态变量赋值;3.调用类的静态方法;4.反射(如Class.forName(“com.shengsiyuan.Test”));5.初始化某个类的子类,则其父类也会被初始化;6.Java虚拟机启动时被标明为启动类的类(JavaTest),直接使用java.exe命令来运行某个主类。

③结束生命周期:在如下几种情况下,Java虚拟机将结束生命周期

    - 执行了System.exit()方法

    - 程序正常执行结束

    - 程序在执行过程中遇到了异常或错误而异常终止

    - 由于操作系统出现错误而导致Java虚拟机进程终止

4.双亲委派模型

双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

双亲委派机制:

1、当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

2、当 ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootstrapClassLoader去完成。

3、如果 BootstrapClassLoader加载失败(例如在 $JAVA_HOME/jre/lib里未查找到该class),会使用 ExtClassLoader来尝试加载;

4、若ExtClassLoader也加载失败,则会使用 AppClassLoader来加载,如果 AppClassLoader也加载失败,则会报出异常 ClassNotFoundException。

三、JAVA内存模型

Java内存模型主要有三大块:堆内存、方法区、栈。堆内存是JVM内存中最大的一块,有年轻代和老年代组成,年轻代又被分为三块:伊甸区(Eden),S0(From Survivor),S1(To Survivor),默认按8:1:1的比例分配。


Java内存模型

1.栈


首先介绍栈。栈是每个线程独有的,每个线程都会有一个单独的栈区域。在线程运行方法的时候,会给这个方法分配一个区域——栈帧,用来存放局部变量表、操作数栈、动态链接、方法出口。


栈帧

局部变量表里的对象放的其实是这个对象在堆中的内存地址;操作数栈中放的是操作数,说白了赋值号=后面的就是操作数;方法出口其实就是方法执行完了之后要到哪里去,也就是调用次方法的地方。


局部变量表的内存地址

此外还有两个java内存模型图里没有体现的区域:程序计数器方法区。

2.程序计数器

程序计数器也是线程私有的区域,每个线程都会分配程序计数器的内存,是用来存放当前线程正在运行或者即将要运行的jvm指令码对应的地址,或者说行号位置。

3.方法区

方法区也就是永久代,jdk1.8之后,Oracle官方管它叫元空间,用来存放常量,静态变量,类元信息(meta data)。方法区在运行时是由所有线程共享的。栈中的局部变量,方法区中的静态变量,如果是对象类型的话,都会指向堆中new出来的对象。


指向堆

类加载其实最终是以类元信息的形式存储在方法区中的,math和math2都是由同一个类new出来的,当对象被new时,都会在对象头中存储一个指向类元信息的指针,也就是Klass Pointer(即红色的箭头)。

4.本地方法栈

一句话描述,可能不太准确,每个线程在运行的时候,如果有运行到本地方法,那么必然也要产生局部变量等,那么就会把这些东西放到本地方法栈。本地方法就是带native关键字的方法,一般是用C语言实现的,底层是调用C语言中的dll库文件,就类似于java中的jar包。现在已经很少用到,因为跨语言交互的方式很多了,比如http接口方式,webservice等。

5.堆

堆是垃圾收集管理的主要区域,也称为GC堆。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。

如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

具体的GC机制及相关算法,将在下一节介绍。

四、垃圾回收机制及算法

垃圾收集通常被称为GC。JVM中,虚拟机栈、程序计数器、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈出栈操作,自动实现了内存的清理。所以,GC主要集中于JVM堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。

1.对象存活判断

引用计数

每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

可达性分析

从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

GC Roots包括:

• 虚拟机栈中引用的对象。

• 方法区中类静态属性实体引用的对象。

• 方法区中常量引用的对象。

• 本地方法栈中JNI引用的对象。

2.GC算法

标记-清除

分为标记、清除阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

缺点很明显:1.效率不高;2.会产生空间碎片。空间碎片太多,会导致需要分配较大对象时,找不到足够连续内存而提前触发GC。


GC算法:标记-清除

复制

将可用内存等量地分为两块,每次只使用其中一块。当这一块内存用完了,还活着的对象复制到另一块上面,需要清理的对象一次清理掉。

优点:简单高效。缺点:以缩小可用内存(只有原来的一半)为代价,而且持续复制长期生存的对象会导致效率降低。


GC算法:复制

标记-压缩

标记过程与“标记-清除”算法一样,只是不过在清理可回收对象前,会让所有存活的对象都向一端移动,然后清理掉端边界以外的内存。


GC算法:标记-压缩(或标记-整理)

分代收集

分代GC的基本假设:绝大部分对象的生命周期都很短暂,存活时间短。

把Java堆分为新生代和老年代,根据其各自的特点采用最适当的GC算法。新生代中,每次GC都有大批对象死去,采用复制算法,只需要付出少量对象的复制成本就可以完成GC;老年代中则采用 标记-清除 或者 标记-压缩 算法来进行回收。

3.垃圾收集器

Serial收集器

最古老,最稳定,也是效率最高的收集器,会产生较长时间的停顿,只用一个线程回收。串行回收,新生代使用复制算法,老年代使用标记-压缩算法。过程中会Stop The World(服务暂停)。


Serial收集器

JVM参数 

-XX:+UseSerialGC

ParNew收集器

Serial收集的多线程版本,新生代并行,老年代依旧串行;新生代使用复制算法,老年代使用标记-压缩


ParNew收集器

JVM参数

-XX:+UseParNewGC //使用ParNew收集器

Parallel收集器

与ParNew收集器类似。Parallel收集器更关注系统的吞吐量。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例;新生代复制算法、老年代标记-压缩。

JVM参数

-XX:+UseParallelGC //使用Parallel收集器+ 老年代串行

Parallel Old收集器

Parallel的老年代版本,使用多线程和标记-压缩算法。JDK1.6之后才有这个收集器。

JVM参数

-XX:+UseParallelOldGC 使用Parallel收集器+ 老年代并行

CMS收集器

CMS收集器的主要设计目标是:低应用停顿时间。而缺点是会产生大量的空间碎片,并发阶段会降低吞吐量。

主要分为7个阶段,其中有两个阶段会发生STW:

Initial Mark(初始化标记):标记出从GC Roots可直达的老年代对象;被新生代存活对象所引用的老年代对象。这个阶段会发生stop-the-world

Concurrent Mark(并发标记):GC线程和应用线程将并发执行,通过遍历第一个阶段(Initial Mark)标记出来的存活对象,继续递归遍历老年代,并标记可直接或间接到达的所有老年代存活对象。此阶段应用线程和GC线程是并发执行的,因此可能产生新的对象或对象关系发生变化,所以把这些对象所在的Card标识为Dirty,后续只扫描Dirty Card的对象,避免扫面整个老年代。

Concurrent Preclean(并发预清理):将会重新扫描前一个阶段标记的Dirty对象,并标记被Dirty对象直接或间接引用的对象,然后清除Card标识。

Concurrent Abortable Preclean(可中止的并发预清理):主要循环的做两件事:处理 From 和 To 区的对象,标记可达的老年代对象;和上一个阶段一样,扫描处理Dirty Card中的对象。

Final Remark(重新标记):重新扫描之前并发处理阶段的所有残留更新对象。遍历新生代对象,重新标记;根据GC Roots,重新标记;遍历老年代的Dirty Card,重新标记。这个阶段会发生stop-the-world

Concurrent Sweep(并发清理):清理所有未被标记的死亡对象,回收被占用的空间。

Concurrent Reset(并发重置):清理并恢复在CMS GC过程中的各种状态,重新初始化CMS相关数据结构,为下一个垃圾收集周期做好准备。

更加详细的CMS收集器图解好文章:

Java之CMS GC的7个阶段

G1收集器

G1相比CMS有一下两个特点:

1.空间整合:

采用标记整理算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。

2.可预测停顿:

建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过M毫秒。

更加详细的G1收集器好文章:

Java Hotspot G1 GC的一些关键技术

五、GC日志分析

1.如何获取GC日志

两种方式:命令动态查看,容器中设置相关参数打印GC日志

命令动态查看

jstat可以用来动态监控JVM内存使用情况,统计垃圾回收的各项信息。常用命令:

jstat -gc

$ jstat -gc 1262

S0C    S1C     S0U     S1U   EC       EU        OC         OU        PC       PU         YGC    YGCT    FGC    FGCT     GCT  

26112.0 24064.0 6562.5  0.0   564224.0 76274.5   434176.0   388518.3  524288.0 42724.7    320    6.417   1      0.398    6.815

也可以设置间隔固定时间打印

jstat -gc 1262 2000 20

每隔2000ms打印1262进程的gc情况

JVM GC日志参数

• -XX:+PrintGC 输出GC日志

• -XX:+PrintGCDetails 输出GC的详细日志

• -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)

• -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2017-09-04T21:53:59.234+0800)

• -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息

• -Xloggc:../logs/gc.log 日志文件的输出路径

2.如何分析GC日志

日志结构

young GC日志结构:

full GC日志结构:

摘录举例:

young GC

2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]

Full GC

2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]

GC分析工具

GChisto,GC Easy

六、JVM调优及相关工具

主要介绍JVM自带的命令:jps, jstat, jmap, jhat, jstack, jinfo

1.jps

显示指定系统内所有的HotSpot虚拟机进程

jps [options]


option参数

-l : 输出主类全名或jar路径

-q : 只输出LVMID

-m : 输出JVM启动时传递给main()的参数

-v : 输出JVM启动时显示指定的JVM参数


示例

$ jps -l -m

28920 org.apache.catalina.startup.Bootstrap start

11589 org.apache.catalina.startup.Bootstrap start

25816 sun.tools.jps.Jps -l -m


2.jstat

用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

jstat [option] LVMID [interval] [count]


参数

[options] : 操作参数

LVMID : 本地虚拟机进程ID

[interval] : 连续输出的时间间隔

[count] : 连续输出的次数


options参数

class class loader的行为统计。Statistics on the behavior of the class loader.

compiler HotSpt JIT编译器行为统计。Statistics of the behavior of the HotSpot Just-in-Time compiler.

gc 垃圾回收堆的行为统计。Statistics of the behavior of the garbage collected heap.

gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计。Statistics of the capacities of the generations and their corresponding spaces.

gcutil 垃圾回收统计概述。Summary of garbage collection statistics.

gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因。Summary of garbage collection statistics (same as -gcutil), with the cause of the last and

gcnew 新生代行为统计。Statistics of the behavior of the new generation.

gcnewcapacity 新生代与其相应的内存空间的统计。Statistics of the sizes of the new generations and its corresponding spaces.

gcold 年老代和永生代行为统计。Statistics of the behavior of the old and permanent generations.

gcoldcapacity 年老代行为统计。Statistics of the sizes of the old generation.

gcpermcapacity 永生代行为统计。Statistics of the sizes of the permanent generation.

printcompilation HotSpot编译方法统计。HotSpot compilation method statistics.


常用参数示例

-class

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


$ jstat -class 11589

Loaded  Bytes  Unloaded  Bytes    Time   

7035  14506.3    0    0.0      3.67


Loaded : 加载class的数量

Bytes : class字节大小

Unloaded : 未加载class的数量

Bytes : 未加载class的字节大小

Time : 加载时间


-compiler

输出JIT编译过的方法数量耗时等


$ jstat -compiler 1262

Compiled Failed Invalid  Time  FailedType FailedMethod   

2573      1      0    47.60          1    org/apache/catalina/loader/WebappClassLoader findResourceInternal 


Compiled : 编译数量

Failed : 编译失败数量

Invalid : 无效数量

Time : 编译耗时

FailedType : 失败类型

FailedMethod : 失败方法的全限定名


-gc

垃圾回收堆的行为统计


$ jstat -gc 1262

S0C    S1C    S0U    S1U  EC      EU        OC        OU        PC      PU        YGC    YGCT    FGC    FGCT    GCT 

26112.0 24064.0 6562.5  0.0  564224.0 76274.5  434176.0  388518.3  524288.0 42724.7    320    6.417  1      0.398    6.815


C即Capacity 总容量,U即Used 已使用的容量

S0C : survivor0区的总容量

S1C : survivor1区的总容量

S0U : survivor0区已使用的容量

S1C : survivor1区已使用的容量

EC : Eden区的总容量

EU : Eden区已使用的容量

OC : Old区的总容量

OU : Old区已使用的容量

PC 当前perm的容量 (KB)

PU perm的使用 (KB)

YGC : 新生代垃圾回收次数

YGCT : 新生代垃圾回收时间

FGC : 老年代垃圾回收次数

FGCT : 老年代垃圾回收时间

GCT : 垃圾回收总消耗时间


$ jstat -gc 1262 2000 20

每隔2000ms输出1262的gc情况,一共输出20次


-gccapacity

同-gc,不过还会输出Java堆各区域使用到的最大、最小空间


$ jstat -gccapacity 1262

NGCMN    NGCMX    NGC    S0C  S1C      EC        OGCMN      OGCMX      OGC        OC      PGCMN    PGCMX    PGC      PC        YGC    FGC

614400.0 614400.0 614400.0 26112.0 24064.0 564224.0  434176.0  434176.0  434176.0  434176.0 524288.0 1048576.0 524288.0 524288.0    320    1 


NGCMN : 新生代占用的最小空间

NGCMX : 新生代占用的最大空间

OGCMN : 老年代占用的最小空间

OGCMX : 老年代占用的最大空间

OGC:当前年老代的容量 (KB)

OC:当前年老代的空间 (KB)

PGCMN : perm占用的最小空间

PGCMX : perm占用的最大空间


-gcutil

同-gc,不过输出的是已使用空间占总空间的百分比


$ jstat -gcutil 28920 

S0    S1    E      O      P    YGC    YGCT    FGC    FGCT    GCT   

12.45  0.00  33.85  0.00  4.44  4      0.242    0    0.000    0.242


-gccause

垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因


$ jstat -gccause 28920 

S0    S1    E      O      P      YGC    YGCT    FGC    FGCT    GCT    LGCC                GCC                 

12.45  0.00  33.85  0.00  4.44      4    0.242    0    0.000    0.242  Allocation Failure  No GC 


LGCC:最近垃圾回收的原因

GCC:当前垃圾回收的原因


-gcnew

统计新生代的行为


-gcnewcapacity

新生代与其相应的内存空间的统计


-gcold

统计老年代的行为


-gcoldcapacity

统计老年代的大小和空间


-gcpermcapacity

永生代行为统计


-printcompilation

hotspot编译方法统计


3.jmap

jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。


jmap [options] LVMID


options参数

dump : 生成堆转储快照

finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象

heap : 显示Java堆详细信息

histo : 显示堆中对象的统计信息

permstat : to print permanent generation statistics

F : 当-dump没有响应时,强制生成dump快照


常用参数示例

-dump

$ jmap -dump:live,format=b,file=dump.hprof 28920 

Dumping heap to /home/xxx/dump.hprof ... 

Heap dump file created

dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名

dump.hprof这个后缀是为了后续可以直接用MAT(Memory Anlysis Tool)打开。


-finalizerinfo

打印等待回收对象的信息


$ jmap -finalizerinfo 28920 

Attaching to process ID 28920, please wait... 

Debugger attached successfully. 

Server compiler detected. 

JVM version is 24.71-b01 

Number of objects pending for finalization: 0

可以看到当前F-QUEUE队列中并没有等待Finalizer线程执行finalizer方法的对象。


-heap

打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况

$ jmap -heap 28920 

Attaching to process ID 28920, 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区内存分布   

    apacity = 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.


-histo

打印堆的对象统计,包括对象数、内存大小等等

$ jmap -histo:live 28920 | more

num    #instances        #bytes  class name

----------------------------------------------

  1:        83613      12012248  <constMethodKlass>

  2:        23868      11450280  [B

  3:        83613      10716064  <methodKlass>

  4:        76287      10412128  [C

  5:          8227        9021176  <constantPoolKlass>

  6:          8227        5830256  <instanceKlassKlass>

  7:          7031        5156480  <constantPoolCacheKlass>

  8:        73627        1767048  java.lang.String

  9:          2260        1348848  <methodDataKlass>

  10:          8856        849296  java.lang.Class

  ....

class name是对象类型,说明如下:

B  byte

C  char

D  double

F  float

I  int

J  long

Z  boolean

[  数组,如[I表示int[]

[L+类名 其他对象


-permstat

打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。


-F

强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。


4.jhat

jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。


5.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 [options] LVMID


options参数

-F : 当正常输出请求不被响应时,强制输出线程堆栈

-l : 除堆栈外,显示关于锁的附加信息

-m : 如果调用到本地方法的话,可以显示C/C++的堆栈


示例

$ jstack -l 11494|more

2016-07-28 13:40:04

Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.71-b01 mixed mode):

"Attach Listener" daemon prio=10 tid=0x00007febb0002000 nid=0x6b6f waiting on condition [0x0000000000000000]

  java.lang.Thread.State: RUNNABLE

  Locked ownable synchronizers:

        - None


"http-bio-8005-exec-2" daemon prio=10 tid=0x00007feb94028000 nid=0x7b8c waiting on condition [0x00007fea8f56e000]

    java.lang.Thread.State: WAITING (parking)

        at sun.misc.Unsafe.park(Native Method)

        - parking to wait for  <0x00000000cae09b80> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)

        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)

        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)

        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)

        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)

        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)

        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

        at java.lang.Thread.run(Thread.java:745)

    Locked ownable synchronizers:

        - None

        .....


6.jinfo

jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数。 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo口令

jinfo [option] [args] LVMID


options参数

-flag : 输出指定args参数的值

-flags : 不需要args参数,输出所有JVM参数的值

-sysprops : 输出系统属性,等同于System.getProperties()


示例

$ jinfo -flag 11494

-XX:CMSInitiatingOccupancyFraction=80


一些常用JVM调优工具

VisualVM,MAT(Memory Analyzer Tool),GC Easy


转载自 关于Jvm知识看这一篇就够了 - 知乎

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352