深入理解JVM
带着问题去学习,jvm分析
1
2
3
4
5
6
7
|
[root@localhost ~]# jstat -gcutil 3461 2000
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 23.72 6.21 53.61 9 1.302 3 5.263 6.564
0.00 0.00 23.72 6.21 53.61 9 1.302 3 5.263 6.564
0.00 0.00 23.72 6.21 53.61 9 1.302 3 5.263 6.564
0.00 0.00 23.72 6.21 53.61 9 1.302 3 5.263 6.564
0.00 0.00 23.72 6.21 53.61 9 1.302 3 5.263 6.564
|
上面是一段通过JVM内建的指令jstat对一个Java应用程序的资源和性能进行实时监控的记录,“3461”是Java应用程序的进程ID,“2000”是指每隔2秒钟采集一次监控数据。
各个数据的含义:
S0:Survivor0 使用的百分比
S1:Survivor1 使用的百分比
E:Eden 使用的百分比
O:老年代 使用的百分比
P:永久代或者持久代 使用的百分比
YGC:Yong GC(Minor GC)次数
YGCT:Yong GC(Minor GC)耗费的时间(单位:秒)
FGC:Full GC(Full GC)次数
FGCT:Full GC(Full GC)耗费的时间(单位:秒)
如果你对上面的数据很熟悉和了解,也知道什么意思,那么下面的内容或许可以跳过,如果不熟悉,下面的内容将帮助你更好的理解。
JVM 体系结构
PC寄存器
JVM会为每一个创建的线程分配一个PC寄存器
大小为一个字节
内容是下一条将执行指令的地址
java方法栈
线程启动时,JVM会为其分配一个Java栈
JVM对java方法栈只有“压栈”,“出栈”的操作,操作的单位是栈帧
栈帧由三部分组成“局部变量区”,“操作数栈“,“栈帧数据区”
方法区
一个JVM只有一个方法区,是所有线程共享的
存放Class的线性二进制流
类信息,该类型的常量池,字段信息,方法的字节码,操作数栈和该方法的栈帧中的局部变量区的大小,异常表,到类ClassLoader的引用,到Class类的应用
方法区大小不固定,可以动态调整
方法区也可以被GC
堆
一个JVM只有一个堆,所有线程共享
存放所有类实例和数组
JVM 的内存构成
上图对应文章开头列出的jvm分析数据中的各个代号意思。
上图是对象数据在jvm内各代的流转,上面各代的内存使用情况是我们优化jvm的关键,如果有任何一个的内存不够用,那么将抛出 OutOfMemorryError,最终导致java进程退出。
GC流程:
1、大部分情况新生对象都存在Eden,当Eden满了,触发YGC;
2、S0 存放 YGC 后存活的对象及 S1 中存活的对象;
3、YGC 完成几次后,如果 S0 空间满了,则存放到 O 中,如果 YGC 几次之后新生代内存还是不足,则抛出:
java.lang.OutOfMemorryError:java heap space;4、O 中对象如果满了,则触发 FGC,如果 FGC 后,年老代内存还是不足,则抛出:
java.lang.OutOfMemorryError:java heap space;5、P 中存放一些class信息等,当系统重要加载的类,反射的类和调用的方法较多时,P 可能会被占满, P 如果满了则触发 FGC,如果 FGC 后仍然满着,则抛出:java.lang.OutOfMemoeryError:PermGen space;
通过jstat工具我们可以看到各代实时的占用情况,下面我们通过jmap来了解jvm当前的配置和使用情况,如下显示:
01
02
03
04
05
06
07
08
09
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
47
48
|
[root@localhost ~]# jmap -heap 7583
Attaching to process ID 7583, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.1-b02
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 6442450944 (6144.0MB)
NewSize = 2147483648 (2048.0MB)
MaxNewSize = 2147483648 (2048.0MB)
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 268435456 (256.0MB)
MaxPermSize = 536870912 (512.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 1382416384 (1318.375MB)
used = 562811192 (536.7385787963867MB)
free = 819605192 (781.6364212036133MB)
40.71213264787232% used
From Space:
capacity = 370540544 (353.375MB)
used = 253155912 (241.42829132080078MB)
free = 117384632 (111.94670867919922MB)
68.32070500765498% used
To Space:
capacity = 382533632 (364.8125MB)
used = 0 (0.0MB)
free = 382533632 (364.8125MB)
0.0% used
PS Old Generation
capacity = 4294967296 (4096.0MB)
used = 107942928 (102.94239807128906MB)
free = 4187024368 (3993.057601928711MB)
2.5132421404123306% used
PS Perm Generation
capacity = 268435456 (256.0MB)
used = 128682560 (122.72125244140625MB)
free = 139752896 (133.27874755859375MB)
47.937989234924316% used
|
上面我们看到了Eden(Eden Space),S0(From Space),S1(To Space),O(PS Old Generation),P(PS Perm Generation)各个内存使用情况,那么接下来我们来看看如何进行上述参数的配置。
JVM 优化的参数设置
首先看一下各代的分配比例及组成:
由上图可以看到:整个堆大小 = 年轻代大小(Eden+S0+S1) + 年老代大小(O)。
各代的内存大小设置:
1、堆的大小可以通过 -Xms 和 -Xmx 来设置,一般将他们设置为相同的大小,目的是避免在每次垃圾回收后重新调整堆的大小,比如 -Xms=2g -Xmx=2g 或者 -Xms=512m -Xmx=512m
2、年轻代大小可以通过 -Xmn 来设置,比如-Xmn=2g 或者 -Xmn512m,此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
3、年老代大小 = 堆大小 – 年轻代大小
4、持久代或者永久代大小可以通过 -XX:PermSize 和 -XX:MaxPermSize 来控制
5、-XX:SurvivorRatio 控制 Eden和Survivor的内存占用比例,默认为8