tonglin0325的个人主页

JVM调优常用命令

1.查看java进程,jps命令可以列出正在运行的虚拟机进程

1
2
3
4
jps -l
1005373 sun.tools.jps.Jps
1000153 org.apache.flume.node.Application

2.查看flume进程java虚拟机的统计信息

1
2
3
4
jstat -gcutil 1028479
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
71.69 0.00 25.63 0.14 96.56 89.78 116 1.074 0 0.000 1.074

某springboot web服务进程java虚拟机的统计信息

1
2
3
4
jstat -gcutil 29
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
76.84 0.00 47.11 55.27 96.06 93.04 28 17.787 4 8.589 26.376

对应指标的中文含义

S0:Survivor0的占用比例
S1:Survivor1的占用比例
E:新生代Eden区的占用比例
O:老年代的占用比例
M:方法区的占用比例
CCS:压缩类空间的占用比例
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

3.查看jvm使用的是什么垃圾收集器

1
2
3
4
5
6
java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=2147483648 -XX:MaxHeapSize=32210157568 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

使用的是并行垃圾收集器

 

JVM内存空间组成

Github:JVM 内存结构

Java 虚拟机的内存空间分为 5 个部分:

1
2
3
4
5
6
程序计数器
Java 虚拟机栈
本地方法栈

方法区

图来自:【JVM学习】——本地方法栈、堆 

 

配置参数

参考:JVM内存调优总结 -Xms -Xmx -Xmn -Xss 参数设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
堆设置
-Xms :初始堆大小
-Xmx :最大堆大小
-XX:NewSize=n :设置年轻代大小
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n :设置持久代大小
收集器设置
-XX:+UseSerialGC :设置串行收集器
-XX:+UseParallelGC :设置并行收集器
-XX:+UseParalledlOldGC :设置并行年老代收集器
-XX:+UseConcMarkSweepGC :设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n :设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n :设置并行收集最大暂停时间
-XX:GCTimeRatio=n :设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode :设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n :设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

 

1.程序计数器

程序计数器(Program Counter Register),在JVM规范中,每个线程都有自己的程序计数器。这是一块比较小的内存空间,存储当前线程正在执行的Java方法的JVM指令地址,即字节码的行号。如果正在执行Native方法,则这个计数器为空。该内存区域是唯一一个在Java虚拟机规范中没有规定任何OOM情况的内存区域。

 

2.Java虚拟机栈

Java虚拟机栈(Java Virtal Machine Stack),同样也是属于线程私有区域,每个线程在创建的时候都会创建一个虚拟机栈,生命周期与线程一致,线程退出时,线程的虚拟机栈也回收。虚拟机栈内部保持一个个的栈帧,每次方法调用都会进行压栈,JVM对栈帧的操作只有出栈和压栈两种,方法调用结束时会进行出栈操作。

 

3.本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈类似,本地方法栈是在调用本地方法时使用的栈,每个线程都有一个本地方法栈。

 

4.堆内存

使用jmap查看JVM的堆内存情况

堆(Heap),几乎所有创建的Java对象实例,都是被直接分配到堆上的。堆被所有的线程所共享,在堆上的区域,会被垃圾回收器做进一步划分,例如新生代、老年代的划分。Java虚拟机在启动的时候,可以使用“Xmx”之类的参数指定堆区域的大小。

堆内存分成 Young Generation(年轻代)和 Old Generation(老年代)

此外,Young Generation(年轻代)还分成 Eden Space , From Space 和 To Space(也叫 Survivor0空间 和 Survivor1空间)

JVM参数设置参考:JVM系列三:JVM参数设置、分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
jmap -heap 29
Attaching to process ID 29, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13

using thread-local object allocation.
Parallel GC with 33 thread(s)

Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 32210157568 (30718.0MB)
NewSize = 715653120 (682.5MB)
MaxNewSize = 10736369664 (10239.0MB)
OldSize = 1431830528 (1365.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
**PS Young Generation**
Eden Space:
capacity = 5859442688 (5588.0MB)
used = 2972839024 (2835.1202239990234MB)
free = 2886603664 (2752.8797760009766MB)
50.73586657120658% used
From Space:
capacity = 505937920 (482.5MB)
used = 388770920 (370.7608413696289MB)
free = 117167000 (111.7391586303711MB)
76.84162515432723% used
To Space:
capacity = 558891008 (533.0MB)
used = 0 (0.0MB)
free = 558891008 (533.0MB)
0.0% used
**PS Old Generation**
capacity = 2802843648 (2673.0MB)
used = 1549069632 (1477.3079223632812MB)
free = 1253774016 (1195.6920776367188MB)
55.26778609664352% used

 

5.方法区

方法区(Method Area)。方法区与堆一样,也是所有的线程所共享,存储被虚拟机加载的元(Meta)数据,包括类信息、常量、静态变量、即时编译器编译后的代码等数据。这里需要注意的是运行时常量池也在方法区中。根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。由于早期HotSpot JVM的实现,将CG分代收集拓展到了方法区,因此很多人会将方法区称为永久代。Oracle JDK8中已永久代移除永久代,同时增加了元数据区(Metaspace)。

 

6.运行时常量池

运行时常量池(Run-Time Constant Pool),这是方法区的一部分,受到方法区内存的限制,当常量池无法再申请到内存时,会抛出OutOfMemoryError异常。

 

7.直接内存

直接内存(Direct Memory),直接内存并不属于Java规范规定的属于Java虚拟机运行时数据区的一部分。Java的NIO可以使用Native方法直接在java堆外分配内存,使用DirectByteBuffer对象作为这个堆外内存的引用。