首先看一下一个java进程的jmap输出:
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
47
48
|
[
lex
@
chou
~
]
$
jmap
-
heap
837
Attaching
to
process
ID
837
,
please
wait
.
.
.
Debugger
attached
successfully
.
Server
compiler
detected
.
JVM
version
is
20.10
-
b01
using
thread
-
local
object
allocation
.
Parallel
GC
with
2
thread
(
s
)
Heap
Configuration
:
MinHeapFreeRatio
=
40
MaxHeapFreeRatio
=
70
MaxHeapSize
=
4294967296
(
4096.0MB
)
NewSize
=
1310720
(
1.25MB
)
MaxNewSize
=
17592186044415
MB
OldSize
=
5439488
(
5.1875MB
)
NewRatio
=
2
SurvivorRatio
=
8
PermSize
=
21757952
(
20.75MB
)
MaxPermSize
=
85983232
(
82.0MB
)
Heap
Usage
:
PS
Young
Generation
Eden
Space
:
capacity
=
41025536
(
39.125MB
)
used
=
18413552
(
17.560531616210938MB
)
free
=
22611984
(
21.564468383789062MB
)
44.883147900858624
%
used
From
Space
:
capacity
=
4325376
(
4.125MB
)
used
=
3702784
(
3.53125MB
)
free
=
622592
(
0.59375MB
)
85.60606060606061
%
used
To
Space
:
capacity
=
4521984
(
4.3125MB
)
used
=
0
(
0.0MB
)
free
=
4521984
(
4.3125MB
)
0.0
%
used
PS
Old
Generation
capacity
=
539820032
(
514.8125MB
)
used
=
108786168
(
103.74657440185547MB
)
free
=
431033864
(
411.06592559814453MB
)
20.152302906758376
%
used
PS
Perm
Generation
capacity
=
85983232
(
82.0MB
)
used
=
60770232
(
57.95500946044922MB
)
free
=
25213000
(
24.04499053955078MB
)
70.67684080542588
%
used
|
然后再用ps看看:
1
2
3
|
[
lex
@
chou
~
]
$
ps
-
p
837
-
o
vsz
,
rss
VSZ
RSS
7794992
3047320
|
关于这里的几个generation网上资料一大把就不细说了,这里算一下求和可以得知前者总共给Java环境分配了644M的内存,而ps输出的VSZ和RSS分别是7.4G和2.9G,这到底是怎么回事呢?
前面jmap输出的内容里,MaxHeapSize 是在命令行上配的,-Xmx4096m,这个java程序可以用到的最大堆内存。
VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大小,产生这个的可能性很多,比如内存映射,共享的动态库,或者向系统申请了更多的堆,都会扩展线性空间大小,要查看一个进程有哪些内存映射,可以使用 pmap 命令来查看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[
lex
@
chou
~
]
$
pmap
-
x
837
837
:
java
Address
Kbytes
RSS
Dirty
Mode
Mapping
0000000040000000
36
4
0
r
-
x
--
java
0000000040108000
8
8
8
rwx
--
java
00000000418c9000
13676
13676
13676
rwx
--
[
anon
]
00000006fae00000
83968
83968
83968
rwx
--
[
anon
]
0000000700000000
527168
451636
451636
rwx
--
[
anon
]
00000007202d0000
127040
0
0
--
--
-
[
anon
]
.
.
.
.
.
.
00007f55ee124000
4
4
0
r
-
xs
-
az
.
png
00007fff017ff000
4
4
0
r
-
x
--
[
anon
]
ffffffffff600000
4
0
0
r
-
x
--
[
anon
]
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
total
kB
7796020
3037264
3023928
|
这里可以看到很多anon,这些表示这块内存是由mmap分配的。
RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小, 在现在这个例子当中,RSZ和实际堆内存占用差了2.3G,这2.3G的内存组成分别为:
- JVM本身需要的内存,包括其加载的第三方库以及这些库分配的内存
- NIO的DirectBuffer是分配的native memory
- 内存映射文件,包括JVM加载的一些JAR和第三方库,以及程序内部用到的。上面 pmap 输出的内容里,有一些静态文件所占用的大小不在Java的heap里,因此作为一个Web服务器,赶紧把静态文件从这个Web服务器中人移开吧,放到nginx或者CDN里去吧。
- JIT, JVM会将Class编译成native代码,这些内存也不会少,如果使用了Spring的AOP,CGLIB会生成更多的类,JIT的内存开销也会随之变大,而且Class本身JVM的GC会将其放到Perm Generation里去,很难被回收掉,面对这种情况,应该让JVM使用ConcurrentMarkSweep GC,并启用这个GC的相关参数允许将不使用的class从Perm Generation中移除, 参数配置: -XX:+UseConcMarkSweepGC -X:+CMSPermGenSweepingEnabled -X:+CMSClassUnloadingEnabled,如果不需要移除而Perm Generation空间不够,可以加大一点: -X:PermSize=256M -X:MaxPermSize=512M
- JNI,一些JNI接口调用的native库也会分配一些内存,如果遇到JNI库的内存泄露,可以使用valgrind等内存泄露工具来检测
- 线程栈,每个线程都会有自己的栈空间,如果线程一多,这个的开销就很明显了
- jmap/jstack 采样,频繁的采样也会增加内存占用,如果你有服务器健康监控,记得这个频率别太高,否则健康监控变成致病监控了。
关于JVM的几个GC堆和GC的情况,可以用jstat来监控,例如监控进程837每隔1000毫秒刷新一次,输出20次:
1
2
3
4
|
[
lex
@
chou
~
]
$
jstat
-
gcutil
837
1000
20
S0
S1
E
O
P
YGC
YGCT
FGC
FGCT
GCT
0.00
80.43
24.62
87.44
98.29
7101
119.652
40
19.719
139.371
0.00
80.43
33.14
87.44
98.29
7101
119.652
40
19.719
139.371
|
几个字段分别含义如下:
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) |
结论
因此如果正常情况下jmap输出的内存占用远小于 RSZ,可以不用太担心,除非发生一些严重错误,比如PermGen空间满了导致OutOfMemoryError发生,或者RSZ太高导致引起系统公愤被OOM Killer给干掉,就得注意了,该加内存加内存,没钱买内存加交换空间,或者按上面列的组成部分逐一排除。
这几个内存指标之间的关系是:VSZ >> RSZ >> Java程序实际使用的堆大小
相关的参考资料:
http://www.ibm.com/developerworks/java/library/j-nativememory-linux/
http://www.oracle.com/technetwork/java/javase/index-137495.html