JVM内存分配性能问题

  1. JVM内存分配不合理最直接的表现就是频繁的GC,这会导致上下文切换,从而降低系统的吞吐量增加系统的响应时间

对象在堆中的生命周期

  1. 在JVM内存模型的堆中,堆被划分为新生代老年代
    • 新生代又被进一步划分为Eden区Survivor区,Survivor区由From SurvivorTo Survivor组成
  2. 当创建一个对象时,对象会被优先分配到新生代的Eden区
    • 此时JVM会给对象定义一个对象年轻计数器-XX:MaxTenuringThreshold
  3. 当Eden空间不足时,JVM将执行新生代的垃圾回收(Minor GC
    • JVM会把存活的对象转移到Survivor中,并且对象年龄+1
    • 对象在Survivor中同样也会经历Minor GC,每经历一次Minor GC,对象年龄都会+1
  4. 如果分配的对象超过了-XX:PetenureSizeThreshold,对象会直接被分配到老年代

查看JVM堆内存分配

  1. 在默认不配置JVM堆内存大小的情况下,JVM根据默认值来配置当前内存大小
  2. 在JDK 1.7中,默认情况下新生代老年代的比例是1:2,可以通过–XX:NewRatio来配置
    • 新生代中的Eden:From Survivor:To Survivor的比例是8:1:1,可以通过-XX:SurvivorRatio来配置
  3. 若在JDK 1.7中开启了-XX:+UseAdaptiveSizePolicy,JVM会动态调整JVM堆中各个区域的大小以及进入老年代的年龄
    • 此时–XX:NewRatio-XX:SurvivorRatio将会失效,而JDK 1.8是默认开启-XX:+UseAdaptiveSizePolicy
    • 在JDK 1.8中,不要随意关闭-XX:+UseAdaptiveSizePolicy,除非对堆内存的划分有明确的规划
    • 每次GC后都会重新计算Eden、From Survivor、To Survivor的大小
      • 计算依据是GC过程中统计的GC时间吞吐量内存占用量
1
2
3
4
5
6
7
8
9
$ java -XX:+PrintFlagsFinal -version | grep HeapSize
uintx ErgoHeapSizeLimit = 0 {product}
uintx HeapSizePerGCThread = 87241520 {product}
uintx InitialHeapSize := 261304192 {product}
uintx LargePageHeapSizeThreshold = 134217728 {product}
uintx MaxHeapSize := 4181721088 {product}
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
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
49
50
51
52
53
54
55
$ jmap -heap 10773
Attaching to process ID 10773, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.65-b04

using thread-local object allocation.
Garbage-First (G1) GC with 12 thread(s)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 268435456 (256.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 17592186044415 MB
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 134217728 (128.0MB)
MaxPermSize = 134217728 (128.0MB)
G1HeapRegionSize = 4194304 (4.0MB)

Heap Usage:
G1 Heap:
regions = 64
capacity = 268435456 (256.0MB)
used = 89813712 (85.65303039550781MB)
free = 178621744 (170.3469696044922MB)
33.45821499824524% used
G1 Young Generation:
Eden Space:
regions = 11
capacity = 163577856 (156.0MB)
used = 46137344 (44.0MB)
free = 117440512 (112.0MB)
28.205128205128204% used
Survivor Space:
regions = 1
capacity = 4194304 (4.0MB)
used = 4194304 (4.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 11
capacity = 100663296 (96.0MB)
used = 39482064 (37.65303039550781MB)
free = 61181232 (58.34696960449219MB)
39.221906661987305% used
Perm Generation:
capacity = 134217728 (128.0MB)
used = 41068592 (39.16606140136719MB)
free = 93149136 (88.83393859863281MB)
30.598485469818115% used

16298 interned Strings occupying 1462984 bytes.

内存泄露 / 内存溢出

  1. 内存泄露:不再使用的对象无法得到及时的回收,持续占用内存空间,从而造成内存空间的浪费
  2. 内存溢出:如内存空间不足,空间不足,方法区空间不足等
  3. 关系:_内存泄露可能导致内存溢出,但内存溢出不一定是内存泄露导致的_

参考资料

Java性能调优实战