首先Java虚拟机中gc的原理,可以参见
http://www.360doc.com/content/12/1023/16/9615799_243296263.shtml
http://ifeve.com/useful-jvm-flags-part-5-young-generation-garbage-collection/
以下是一些个人总结和测试。
划分新生代和老年代,这样只在新生代分配内存,从而简化了新对象的分配。另外新生代和老年代使用不同的GC算法,可以更有效的清除不再需要的对象。
从上图可以看出,JVM内存由young+old+permanent组成,JVM又进一步将Young分成了eden,from survivor和to survivor三个区域。新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象熬过一定的GC的次数,会被移动到老年代。老年代一般是一些系统级(线程库,classloader等) 的对象,官方推荐新生代占堆大小的3/8,而survivor区各占新生代的1/10。
很多对象的生存时间都很短,而新生对象很少引用生存时间长的对象。所以,GC会频繁访问新生代对象,执行Minor GC。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”(即执行major/FULL GC)。
新生代的GC使用复制算法。在GC前To survivor区保持清空,对象保存在Eden和From survivor区中,GC运行时,Eden中的幸存对象被复制到 To survivor区。针对 From survivor取中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To survivor区。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From survivor区中只保存死对象,可以被视为全部清空。如果在复制过程中To survivor区被填满了,剩余的对象会被复制到老年代中。最后 From和To会对换。
上图演示GC过程,黄色表示死对象,绿色表示剩余空间,红色表示幸存对象
如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。所以需要通过不断的测试调优,找到一个合适的JVM参数。
-Xms:初始堆大小。只要启动,就占用的堆大小
-Xmx:最大堆大小。java.lang.OutOfMemoryError: Java heap这个错误可以通过配置-Xms和-Xmx参数来设置
-Xss:栈大小分配。栈是每个线程私有的区域,通常只有几百K大小,决定了函数调用的深度,而局部变量、参数都分配到栈上。当出现大量局部变量,递归时,会发生栈空间OOM(java.lang.StackOverflowError)之类的错误。
-XX:NewSize=n:设置新生代大小的绝对值
-XX:NewRatio=n: 设置年轻代和年老代的比值。比如设置为3,则新生代:老年代=1:3,新生代占1/4的总heap大小。
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有from和to两个。比如设置为8时,那么eden:from:to=8:1:1
-XX:MaxPermSize=n:设置持久代大小 ;java.lang.OutOfMemoryError: PermGen space这个OOM错误需要合理调大PermSize和MaxPermSize大小。
-XX:HeapDumpOnOutOfMemoryError:发生OOM时转储堆到文件,这是一个非常好的诊断方法。
-XX:HeapDumpPath:导出堆的转储文件路径
-XX:OnOutOfMemoryError:OOM时,执行一个脚本,比如发送邮件报警,重启程序。后面跟着一个脚本的路径。
第一次分配5M,没有超过xms。第二次再次分配5M,total mem会增加。第三次再申请40M,超过xmx限制所以报了OOM错误。
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
|
public
class
JVMTest
{
public
static
void
main
(
String
args
[
]
)
{
//=====================Begin=========================
System
.
out
.
print
(
"Xmx="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
maxMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
System
.
out
.
print
(
"free mem="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
freeMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
System
.
out
.
print
(
"total mem="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
totalMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
//=====================First Allocated=========================
System
.
out
.
println
(
"5MB array allocated"
)
;
byte
[
]
b1
=
new
byte
[
5
*
1024
*
1024
]
;
System
.
out
.
print
(
"Xmx="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
maxMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
System
.
out
.
print
(
"free mem="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
freeMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
System
.
out
.
print
(
"total mem="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
totalMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
//=====================Second Allocated=========================
System
.
out
.
println
(
"10MB array allocated"
)
;
byte
[
]
b2
=
new
byte
[
10
*
1024
*
1024
]
;
System
.
out
.
print
(
"Xmx="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
maxMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
System
.
out
.
print
(
"free mem="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
freeMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
System
.
out
.
print
(
"total mem="
)
;
System
.
out
.
println
(
Runtime
.
getRuntime
(
)
.
totalMemory
(
)
/
1024.0
/
1024
+
"M"
)
;
//=====================OOM=========================
System
.
out
.
println
(
"OOM!!!"
)
;
System
.
gc
(
)
;
byte
[
]
b3
=
new
byte
[
40
*
1024
*
1024
]
;
}
}
|
以50m XMX和10m XMS的运行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
D
:
\
>
java
-
Xmx50m
-
Xms10m
JVMTest
Xmx
=
44.5M
free
mem
=
9.804550170898438M
total
mem
=
10.5M
5MB
array
allocated
Xmx
=
44.5M
free
mem
=
4.804534912109375M
total
mem
=
10.5M
10MB
array
allocated
Xmx
=
44.5M
free
mem
=
5.3045196533203125M
total
mem
=
21.0M
OOM
!
!
!
Exception
in
thread
"main"
java
.
lang
.
OutOfMemoryError
:
Java
heap
space
at
JVMTest
.
main
(
JVMTest
.
java
:
43
)
|
1
2
3
4
5
6
7
|
public
class
JVMXmn1
{
public
static
void
main
(
String
args
[
]
)
{
byte
[
]
b
=
null
;
for
(
int
i
=
0
;
i
<
10
;
i
++
)
b
=
new
byte
[
1
*
1024
*
1024
]
;
}
}
|
下面按1m的新生代设置,这时对象大于新生代大小,会直接创建在老年代。新生代没有使用。没有触发gc
1
2
3
4
5
6
7
8
9
10
|
D
:
\
>
java
-
Xmx20m
-
Xms20m
-
Xmn1m
-
XX
:
+
PrintGCDetails
JVMXmn1
Heap
PSYoungGen
total
512K
,
used
0K
[
0x00000000fff00000
,
0x0000000100000000
,
0x0000000100000000
)
eden
space
0K
,
-
2147483648
%
used
[
0x00000000fff00000
,
0x00000000fff00000
,
0x00000000fff00000
)
from
space
512K
,
0
%
used
[
0x00000000fff80000
,
0x00000000fff80000
,
0x0000000100000000
)
to
space
512K
,
0
%
used
[
0x00000000fff00000
,
0x00000000fff00000
,
0x00000000fff80000
)
ParOldGen
total
19456K
,
used
10836K
[
0x00000000fec00000
,
0x00000000fff00000
,
0x00000000fff00000
)
object
space
19456K
,
55
%
used
[
0x00000000fec00000
,
0x00000000ff695348
,
0x00000000fff00000
)
PSPermGen
total
21504K
,
used
2442K
[
0x00000000f9a00000
,
0x00000000faf00000
,
0x00000000fec00000
)
object
space
21504K
,
11
%
used
[
0x00000000f9a00000
,
0x00000000f9c628d8
,
0x00000000faf00000
)
|
下面按15m的新生代设置,全部分配在eden区,老年代没有使用,没有触发gc。
1
2
3
4
5
6
7
8
9
10
|
D
:
\
>
java
-
Xmx20m
-
Xms20m
-
Xmn15m
-
XX
:
+
PrintGCDetails
JVMXmn1
Heap
PSYoungGen
total
13824K
,
used
11525K
[
0x00000000ff100000
,
0x0000000100000000
,
0x0000000100000000
)
eden
space
12288K
,
93
%
used
[
0x00000000ff100000
,
0x00000000ffc417c8
,
0x00000000ffd00000
)
from
space
1536K
,
0
%
used
[
0x00000000ffe80000
,
0x00000000ffe80000
,
0x0000000100000000
)
to
space
1536K
,
0
%
used
[
0x00000000ffd00000
,
0x00000000ffd00000
,
0x00000000ffe80000
)
ParOldGen
total
5120K
,
used
0K
[
0x00000000fea00000
,
0x00000000fef00000
,
0x00000000ff100000
)
object
space
5120K
,
0
%
used
[
0x00000000fea00000
,
0x00000000fea00000
,
0x00000000fef00000
)
PSPermGen
total
21504K
,
used
2442K
[
0x00000000f9800000
,
0x00000000fad00000
,
0x00000000fea00000
)
object
space
21504K
,
11
%
used
[
0x00000000f9800000
,
0x00000000f9a628d8
,
0x00000000fad00000
)
|
下面按8m的新生代设置,触发了一次gc,由于from和to的大小小于1M的对象大小,eden区会直接进入老年代。
1
2
3
4
5
6
7
8
9
10
11
|
D
:
\
>
java
-
Xmx20m
-
Xms20m
-
Xmn8m
-
XX
:
+
PrintGCDetails
JVMXmn1
[
GC
[
PSYoungGen
:
5954K
->
536K
(
7168K
)
]
5954K
->
1560K
(
19456K
)
,
0.0021655
secs
]
[
Times
:
user
=
0.00
sys
=
0.00
,
real
=
0.00
secs
]
Heap
PSYoungGen
total
7168K
,
used
5971K
[
0x00000000ff800000
,
0x0000000100000000
,
0x0000000100000000
)
eden
space
6144K
,
88
%
used
[
0x00000000ff800000
,
0x00000000ffd4ecb8
,
0x00000000ffe00000
)
from
space
1024K
,
52
%
used
[
0x00000000ffe00000
,
0x00000000ffe86010
,
0x00000000fff00000
)
to
space
1024K
,
0
%
used
[
0x00000000fff00000
,
0x00000000fff00000
,
0x0000000100000000
)
ParOldGen
total
12288K
,
used
1024K
[
0x00000000fec00000
,
0x00000000ff800000
,
0x00000000ff800000
)
object
space
12288K
,
8
%
used
[
0x00000000fec00000
,
0x00000000fed00010
,
0x00000000ff800000
)
PSPermGen
total
21504K
,
used
2445K
[
0x00000000f9a00000
,
0x00000000faf00000
,
0x00000000fec00000
)
object
space
21504K
,
11
%
used
[
0x00000000f9a00000
,
0x00000000f9c63400
,
0x00000000faf00000
)
|
下面按7m的新生代设置,from和to的大小可以为1/xmn,大于触发了3次新生代gc,一共回收了7M左右的空间,最后剩余3M在系统当中,没有使用老年代。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
D
:
\
>
java
-
Xmx20m
-
Xms20m
-
Xmn7m
-
XX
:
SurvivorRatio
=
2
-
XX
:
+
PrintGCDetails
JVMXmn1
[
GC
[
PSYoungGen
:
3785K
->
1512K
(
5632K
)
]
3785K
->
1568K
(
18944K
)
,
0.0024118
secs
]
[
Times
:
user
=
0.00
sys
=
0.00
,
real
=
0.00
secs
]
[
GC
[
PSYoungGen
:
4755K
->
1528K
(
5632K
)
]
4811K
->
1584K
(
18944K
)
,
0.0013799
secs
]
[
Times
:
user
=
0.00
sys
=
0.00
,
real
=
0.00
secs
]
[
GC
[
PSYoungGen
:
4631K
->
1496K
(
5632K
)
]
4687K
->
1552K
(
18944K
)
,
0.0010990
secs
]
[
Times
:
user
=
0.00
sys
=
0.00
,
real
=
0.00
secs
]
Heap
PSYoungGen
total
5632K
,
used
2561K
[
0x00000000ff900000
,
0x0000000100000000
,
0x0000000100000000
)
eden
space
4096K
,
26
%
used
[
0x00000000ff900000
,
0x00000000ffa0a448
,
0x00000000ffd00000
)
from
space
1536K
,
97
%
used
[
0x00000000ffd00000
,
0x00000000ffe76020
,
0x00000000ffe80000
)
to
space
1536K
,
0
%
used
[
0x00000000ffe80000
,
0x00000000ffe80000
,
0x0000000100000000
)
ParOldGen
total
13312K
,
used
56K
[
0x00000000fec00000
,
0x00000000ff900000
,
0x00000000ff900000
)
object
space
13312K
,
0
%
used
[
0x00000000fec00000
,
0x00000000fec0e000
,
0x00000000ff900000
)
PSPermGen
total
21504K
,
used
2445K
[
0x00000000f9a00000
,
0x00000000faf00000
,
0x00000000fec00000
)
object
space
21504K
,
11
%
used
[
0x00000000f9a00000
,
0x00000000f9c63400
,
0x00000000faf00000
)
|
新生代占一半大小(10m),幸存区为3:1:1(6m:2m,2m),触发了1次gc,回收了7m左右空间,没有使用老年代。对于这种临时对象,减少老年代的使用是gc优化的关键。
1
2
3
4
5
6
7
8
9
10
11
|
D
:
\
>
java
-
Xmx20m
-
Xms20m
-
XX
:
NewRatio
=
1
-
XX
:
SurvivorRatio
=
3
-
XX
:
+
PrintGCDetails
JVMXmn1
[
GC
[
PSYoungGen
:
5954K
->
1624K
(
8192K
)
]
5954K
->
1624K
(
18432K
)
,
0.0023152
secs
]
[
Times
:
user
=
0.00
sys
=
0.00
,
real
=
0.00
secs
]
Heap
PSYoungGen
total
8192K
,
used
7059K
[
0x00000000ff600000
,
0x0000000100000000
,
0x0000000100000000
)
eden
space
6144K
,
88
%
used
[
0x00000000ff600000
,
0x00000000ffb4ecb8
,
0x00000000ffc00000
)
from
space
2048K
,
79
%
used
[
0x00000000ffc00000
,
0x00000000ffd96020
,
0x00000000ffe00000
)
to
space
2048K
,
0
%
used
[
0x00000000ffe00000
,
0x00000000ffe00000
,
0x0000000100000000
)
ParOldGen
total
10240K
,
used
0K
[
0x00000000fec00000
,
0x00000000ff600000
,
0x00000000ff600000
)
object
space
10240K
,
0
%
used
[
0x00000000fec00000
,
0x00000000fec00000
,
0x00000000ff600000
)
PSPermGen
total
21504K
,
used
2445K
[
0x00000000f9a00000
,
0x00000000faf00000
,
0x00000000fec00000
)
object
space
21504K
,
11
%
used
[
0x00000000f9a00000
,
0x00000000f9c63400
,
0x00000000faf00000
)
|
^^