115
Sun JDK 1.6 GC Garbage Collector http://bluedavy.com 2010-05-13 V0.2 2010-05-19 V0.5 2010-06-01 V0.8 ppt 中中中中中中中 JVM 中中 Sun JDK 1.6.0

Sun jdk-1.6-gc

  • Upload
    -

  • View
    1.394

  • Download
    0

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Sun jdk-1.6-gc

Sun JDK 1.6 GC( Garbage Collector )

http://bluedavy.com2010-05-13 V0.2

2010-05-19 V0.52010-06-01 V0.8

ppt 中未特别强调的 JVM 均指 Sun JDK 1.6.0

Page 2: Sun jdk-1.6-gc

Java :自动内存管理 为什么还需要学习 GC ?

OOM? GC 成为支撑更高并发量的瓶颈 ?

Page 3: Sun jdk-1.6-gc

only 介绍

使用通常问题查找Tuning实现

Page 4: Sun jdk-1.6-gc

GC : Garbage Collector

不是只负责内存回收

还决定了内存分配

Page 5: Sun jdk-1.6-gc

使用Hotspot 是如何分配内存的

Hotspot 什么时候回收内存

Page 6: Sun jdk-1.6-gc

内存结构

PC寄存

局部变量区

操作数栈

栈帧

JVM 方法栈 JVM 堆

本地方法栈

JVM 方法区

-Xss

-XX:PermSize –XX:MaxPermSize

-Xms -Xmx

备注:在 Hotspot 中本地方法栈和 JVM 方法栈是同一个,因此也可用 -Xss 控制

Page 7: Sun jdk-1.6-gc

内存分配1 、堆上分配 大多数情况在 eden 上分配,偶尔会直接在 old 上分配

细节取决于 GC 的实现 这里最重要的优化是 TLAB

2 、栈上分配 原子类型的局部变量

或基于 EA 后标量替换转变为原子类型的局部变量 3 、堆外分配 DirectByteBuffer 或直接使用 Unsafe.allocateMemory ,但不推荐这种方式

Page 8: Sun jdk-1.6-gc

内存回收( Garbage Collection )

GC 要做的是将那些 dead 的对象所占用的内存回收掉;

1 、 Hotspot 认为没有引用的对象是 dead 的

2 、 Hotspot 将引用分为四种 Strong 、 Soft 、 Weak 、 Phantom Strong 即默认通过 Object o=new Object() 这种方式赋值的引用; Soft 、 Weak 、 Phantom 这三种则都是继承 Reference ; 在 Full GC 时会对 Reference 类型的引用进行特殊处理:

Soft :内存不够时一定会被 GC 、长期不用也会被 GC ,可通过 -XX:SoftRefLRUPolicyMSPerMB 来设置; Weak :一定会被 GC ,当被 mark 为 dead ,会在 ReferenceQueue 中通知; Phantom :本来就没引用,当从 jvm heap 中释放,会通知。

Page 9: Sun jdk-1.6-gc

内存回收

经 IBM 研究,通常运行的程序有 98% 的对象是临时对象,因此Sun Hotspot 对 JVM 堆采用了分代的方式来管理,以提升 GC 的效率。

Page 10: Sun jdk-1.6-gc

Eden S0 S1 Old Generation

New Generation

JVM 堆:分代

-XX:SurvivorRatio

-Xmn

备注:通常将对新生代进行的回收称为 Minor GC ;对旧生代进行的回收称为 Major GC ,但由于Major GC 除并发 GC 外均需对整个堆进行扫描和回收,因此又称为 Full GC 。

Page 11: Sun jdk-1.6-gc
Page 12: Sun jdk-1.6-gc

串行 GC(Serial

Copying)

并行回收 GC(Parallel

Scavenge)

并行 GC(ParNew)

新生代可用 GC

我该用哪个呢?

Page 13: Sun jdk-1.6-gc

新生代可用 GC— 串行1. client 模式下默认 GC 方式,也可通过 -XX:+UseSerialGC 来强制指定;

2. eden 、 s0 、 s1 的大小通过 -XX:SurvivorRatio 来控制,默认为 8 ,含义 为 eden:s0 的比例,启动后可通过 jmap –heap [pid] 查看。

Page 14: Sun jdk-1.6-gc

新生代可用 GC— 串行默认情况下,仅在 TLAB 或 eden 上分配,只有两种状况会在旧生代分配:1 、需要分配的大小超过 eden space 大小;2 、在配置了 PretenureSizeThreshold 的情况下,对象大小大于此值。

public class SerialGCDemo{ public static void main(String[] args) throws Exception{ byte[] bytes=new byte[1024*1024*2]; byte[] bytes2=new byte[1024*1024*2]; byte[] bytes3=new byte[1024*1024*2]; Thread.sleep(3000); byte[] bytes4=new byte[1024*1024*4]; Thread.sleep(3000); }}

-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC

-Xms20M –Xmx20M –Xmn10M -XX:PretenureSizeThreshold=3145728 –XX:+UseSerialGC

Page 15: Sun jdk-1.6-gc

新生代可用 GC— 串行当 eden space 空间不足时触发。

public class SerialGCDemo{ public static void main(String[] args) throws Exception{ byte[] bytes=new byte[1024*1024*2]; byte[] bytes2=new byte[1024*1024*2]; byte[] bytes3=new byte[1024*1024*2]; System.out.println(“step 1"); byte[] bytes4=new byte[1024*1024*2]; Thread.sleep(3000); System.out.println(“step 2"); byte[] bytes5=new byte[1024*1024*2]; byte[] bytes6=new byte[1024*1024*2]; System.out.println(“step 3"); byte[] bytes7=new byte[1024*1024*2]; Thread.sleep(3000); }}

-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC

Page 16: Sun jdk-1.6-gc

新生代可用 GC— 串行上面示例之所以会是触发一次 minor 和一次 full ,在于 Serial GC 的这个规则:

在回收前 Serial GC 会先检测之前每次 Minor GC 时晋升到旧生代的平均大小是否大

于旧生代的剩余空间,如大于,则直接触发 full ,如小于,则取决于HandlePromotionFailure 的设置。

Page 17: Sun jdk-1.6-gc

新生代可用 GC— 串行 public class SerialGCDemo{ public static void main(String[] args) throws Exception{ byte[] bytes=new byte[1024*1024*2]; byte[] bytes2=new byte[1024*1024*2]; byte[] bytes3=new byte[1024*1024*2]; System.out.println("step 1"); bytes=null; byte[] bytes4=new byte[1024*1024*2]; Thread.sleep(3000); System.out.println("step 2"); byte[] bytes5=new byte[1024*1024*2]; byte[] bytes6=new byte[1024*1024*2]; bytes4=null; bytes5=null; bytes6=null; System.out.println("step 3"); byte[] bytes7=new byte[1024*1024*2]; Thread.sleep(3000); }}

-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC

-Xms20M –Xmx20M –Xmn10M -XX:-HandlePromotionFailure –XX:+UseSerialGC

Page 18: Sun jdk-1.6-gc

新生代可用 GC— 串行上面示例在两个参数时执行效果之所以不同,在于 Serial GC 的这个规则:

触发 Minor GC 时:之前 Minor GC 晋级到 old 的平均大小 < 旧生代剩余空间 < eden+from 使用空

间当 HandlePromotionFailure 为 true ,则仅触发 minor gc ,如为 false ,则触发

full 。

Page 19: Sun jdk-1.6-gc

新生代可用 GC— 串行新生代对象晋升到旧生代的规则

1 、经历多次 minor gc 仍存活的对象,可通过以下参数来控制: 以 MaxTenuringThreshold 值为准,默认为 15 。

2 、 to space 放不下的,直接放入旧生代;

Page 20: Sun jdk-1.6-gc

新生代可用 GC— 串行public class SerialGCThreshold{ public static void main(String[] args) throws Exception{ SerialGCMemoryObject object1=new SerialGCMemoryObject(1); SerialGCMemoryObject object2=new SerialGCMemoryObject(8); SerialGCMemoryObject object3=new SerialGCMemoryObject(8); SerialGCMemoryObject object4=new SerialGCMemoryObject(8); object2=null; object3=null; SerialGCMemoryObject object5=new SerialGCMemoryObject(8); Thread.sleep(4000); object2=new SerialGCMemoryObject(8); object3=new SerialGCMemoryObject(8); object2=null; object3=null; object5=null; SerialGCMemoryObject object6=new SerialGCMemoryObject(8); Thread.sleep(5000); }}class SerialGCMemoryObject{ private byte[] bytes=null; public SerialGCMemoryObject(int multi){ bytes=new byte[1024*256*multi]; }}

-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC

-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC

-XX:MaxTenuringThres

hold=1

Page 21: Sun jdk-1.6-gc

新生代可用 GC— 串行把上面代码中的 object1 修改为如下: SerialGCMemoryObject object1=new SerialGCMemoryObject(2);

-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC

Page 22: Sun jdk-1.6-gc

新生代可用 GC— 串行上面示例中 object1 在第二次 minor gc 时直接转入了 old ,在于 Serial GC 的这个规则:

每次 Minor GC 后会重新计算 TenuringThreshold(第一次以 MaxTenuringThreshold 为准)

计算的规则为:累积每个 age 中的字节,当这个累计值 > To Space 的一半时,对比此时的 age 和MaxTenuringThreshold ,取其中更小的值。

可通过 PrintTenuringDistribution 来查看下次 minor gc 时的 TenuringThreshold值: Desired survivor size 524288 bytes, new threshold 1 (max 15) ,其中的 new threshold 1 即为新的 TenuringThreshold 的值。

例如在上面的例子中:当第一次 Minor GC 结束时,遍历 age table ,当累积 age 1 的字节后,发现此时所占用的字节数 > To Space 的一半,因此将 TenuringThreshold 赋值为 1 ,下次Minor GC 时即把 age 超过 1 的对象全部转入 old 。

Page 23: Sun jdk-1.6-gc

JVM 新生代可用 GC— 串行

[GC [DefNew: 11509K->1138K(14336K), 0.0110060 secs] 11509K->1138K(38912K),

0.0112610 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]

Page 24: Sun jdk-1.6-gc

新生代可用 GC—ParNew1. CMS GC 时默认采用,也可采用 -XX:+UseParNewGC 强制指定;

2. eden 、 s0 、 s1 的大小通过 -XX:SurvivorRatio 来控制,默认为 8 ,含义 为 eden:s0 的比例。

默认情况下其内存分配和回收和 Serial 完全相同,只是回收的时候为多线程而已,但一旦开启 -XX:+UseAdaptiveSizePolicy 则有些不同。

Page 25: Sun jdk-1.6-gc

新生代可用 GC—ParNew

[GC [ParNew: 11509K->1152K(14336K), 0.0129150 secs] 11509K->1152K(38912K),

0.0131890 secs] [Times: user=0.05 sys=0.02, real=0.02 secs]

如启动参数上设置了 -XX:+UseAdaptiveSizePolicy ,则会输出 [GC [ASParNew: 7495K->120K(9216K), 0.0403410 secs] 7495K-

>7294K(19456K), 0.0406480 secs] [Times: user=0.06 sys=0.15, real=0.04 secs]

Page 26: Sun jdk-1.6-gc

新生代可用 GC—PS1. server 模式时默认的 GC 方式,也可采用 -XX:+UseParallelGC 强制指定;

2. eden 、 s0 、 s1 的大小可通过 -XX:SurvivorRatio 来控制,但默认情况下以 -XX:InitialSurivivorRatio 为准,此值默认为 8 ,代表的为新生代大小 : s0 ,这点要特别注意。

Page 27: Sun jdk-1.6-gc

新生代可用 GC—PS大多数情况下,会在 TLAB 或 eden 上分配。

如下一段代码:

public class PSGCDemo{ public static void main(String[] args) throws Exception{ byte[] bytes=new byte[1024*1024*2]; byte[] bytes2=new byte[1024*1024*2]; byte[] bytes3=new byte[1024*1024*2]; Thread.sleep(3000); byte[] bytes4=new byte[1024*1024*4]; Thread.sleep(3000); }}

-Xms20M –Xmx20M –Xmn10M –XX:SurvivorRatio=8 –XX:+UseParallelGC

Page 28: Sun jdk-1.6-gc

新生代可用 GC—PS上面示例中的 bytes4 之所以会直接在旧生代分配,在于 PS GC 的这个规则:

当 TLAB 、 eden 上分配都失败时,判断需要分配的内存大小是否 >= edenspace 的一半大小,如是就直接在旧生代分配。

Page 29: Sun jdk-1.6-gc

新生代可用 GC—PSeden space 分配不下,且需要分配的对象大小未超过 eden space 的一半或 old 区分配失败,触发回收;

public class PSGCDemo{ public static void main(String[] args) throws Exception{ byte[] bytes=new byte[1024*1024*2]; byte[] bytes2=new byte[1024*1024*2]; byte[] bytes3=new byte[1024*1024*2]; System.out.println(“step 1"); byte[] bytes4=new byte[1024*1024*2]; Thread.sleep(3000); System.out.println(“step 2"); byte[] bytes5=new byte[1024*1024*2]; byte[] bytes6=new byte[1024*1024*2]; System.out.println(“step 3"); byte[] bytes7=new byte[1024*1024*2]; Thread.sleep(3000); }}

-Xms20M –Xmx20M –Xmn10M –XX:SurvivorRatio=8 –XX:+UseParallelGC-XX:+PrintGCDetails –XX:verbose:gc

Page 30: Sun jdk-1.6-gc

新生代可用 GC—PS上面示例之所以会是触发一次 minor 和两次 full ,在于 PS GC 的这个规则:

1 、在回收前 PS GC 会先检测之前每次 PS GC 时晋升到旧生代的平均大小是否大于

旧生代的剩余空间,如大于,则直接触发 full ;

2 、在回收后,也会按上面规则进行检测。

Page 31: Sun jdk-1.6-gc

新生代可用 GC—PS新生代对象晋升到旧生代的规则

1 、经历多次 minor gc 仍存活的对象,可通过以下参数来控制: AlwaysTenure ,默认 false ,表示只要 minor GC 时存活,就晋升到旧生代; NeverTenure ,默认 false ,表示永不晋升到旧生代; 上面两个都没设置的情况下,如 UseAdaptiveSizePolicy ,启动时以 InitialTenuringThreshold 值作为存活次数的阈值,在每次 ps gc 后会动态调

整如不使用 UseAdaptiveSizePolicy ,则以 MaxTenuringThreshold 为准。

2 、 to space 放不下的,直接放入旧生代;

Page 32: Sun jdk-1.6-gc

新生代可用 GC—PS

在回收后,如 UseAdaptiveSizePolicy , PS GC 会根据运行状况动态调整eden 、 to

以及 TenuringThreshold 的大小。不希望动态调整可设置 -XX:-UseAdaptiveSizePolicy 。如希望跟踪每次的变化情况,可在启动参数上增加: PrintAdaptiveSizePolicy

Page 33: Sun jdk-1.6-gc

新生代可用 GC—PS

[GC [PSYoungGen: 11509K->1184K(14336K)] 11509K->1184K(38912K), 0.0113360 secs]

[Times: user=0.03 sys=0.01, real=0.01 secs]

Page 34: Sun jdk-1.6-gc

串行 GC(Serial MSC)

并行 MS GC(Parallel MSC)

旧生代可用的 GC

并发 GC(CMS)

我该用哪个呢?

并行 Compacting GC(Parallel Compacting)

Page 35: Sun jdk-1.6-gc

旧生代可用 GC— 串行client 方式下默认 GC 方式,可通过 -XX:+UseSerialGC 强制指定。

Page 36: Sun jdk-1.6-gc

旧生代可用 GC— 串行触发机制1 、 old gen 空间不足;2 、 perm gen 空间不足;3 、 minor gc 时的悲观策略;4 、 minor GC 后在 eden 上分配内存仍然失败;5 、执行 heap dump 时;6 、外部调用 System.gc ,可通过 -XX:+DisableExplicitGC 来禁止。

ps: 如 CollectGen0First 为 true (默认为 false ),则先执行 minor GC ;

Page 37: Sun jdk-1.6-gc

旧生代可用 GC— 串行[Full GC [Tenured: 9216K->4210K(10240K), 0.0066570 secs]

16584K->4210K(19456K), [Perm : 1692K->1692K(16384K)], 0.0067070 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

Page 38: Sun jdk-1.6-gc

旧生代可用 GC— 并行 MSC1. server 模式下默认 GC 方式,可通过 -XX:+UseParallelGC 强制指定;

2. 并行的线程数 cpu core<=8 ? cpu core : 3+(cpu core*5)/8 或通过 -XX:ParallelGCThreads=x 来强制指定。

Page 39: Sun jdk-1.6-gc

旧生代可用 GC— 并行 MSC触发机制和串行完全相同

ps: 如 ScavengeBeforeFullGC 为 true (默认值),则先执行 minor GC ;

Page 40: Sun jdk-1.6-gc

旧生代可用 GC— 并行 MSC

[Full GC [PSYoungGen: 1208K->0K(8960K)] [PSOldGen: 6144K->7282K(10240K)] 7352K->7282K(19200K) [PSPermGen: 1686K->1686K(16384K)], 0.0165880 secs] [Times: user=0.01 sys=0.01, real=0.02 secs]

Page 41: Sun jdk-1.6-gc

旧生代可用 GC— 并行 Compacting

1. 可通过 -XX:+UseParallelOldGC 强制指定;

2. 并行的线程数 cpu core<=8 ? cpu core : 3+(cpu core*5)/8 或通过 -XX:ParallelGCThreads=x 来强制指定。

Page 42: Sun jdk-1.6-gc

触发机制和并行 MSC 完全相同

旧生代可用 GC— 并行 Compacting

Page 43: Sun jdk-1.6-gc

[Full GC [PSYoungGen: 1224K->0K(8960K)] [ParOldGen: 6144K->7282K(10240K)] 7368K->7282K(19200K) [PSPermGen: 1686K->1685K(16384K)], 0.0223510 secs] [Times: user=0.02 sys=0.06, real=0.03 secs]

旧生代可用 GC— 并行 Compacting

Page 44: Sun jdk-1.6-gc

旧生代可用 GC— 并发可通过 -XX:+UseConcMarkSweepGC 来强制指定,并发的线程数默认为 :( 并行 GC 线程数 +3)/4 ,也可通过 ParallelCMSThreads 指定;

Page 45: Sun jdk-1.6-gc

旧生代可用 GC— 并发触发机制

1 、当旧生代空间使用到一定比率时触发; JDK V 1.6 中默认为 92% ,可通过 PrintCMSInitiationStatistics (此参数在

V 1.5 中不能用)来查看这个值到底是多少; 可通过 CMSInitiatingOccupancyFraction 来强制指定,默认值并不是赋值在

了这个值上,是根据如下公式计算出来的: ((100 - MinHeapFreeRatio) +(double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0)/ 100.0; MinHeapFreeRatio 默认值: 40 CMSTriggerRatio 默认值: 80

2 、当 perm gen 采用 CMS 收集且空间使用到一定比率时触发; perm gen 采用 CMS 收集需设置: -XX:+CMSClassUnloadingEnabled JDK V 1.6 中默认为 92% ; 可通过 CMSInitiatingPermOccupancyFraction 来强制指定,同样,它是根据

如下公式计算出来的: ((100 - MinHeapFreeRatio) +(double)(CMSTriggerPermRatio* MinHeapFreeRatio) / 100.0)/ 100.0; MinHeapFreeRatio 默认值: 40 CMSTriggerPermRatio 默认值: 80

Page 46: Sun jdk-1.6-gc

旧生代可用 GC— 并发触发机制

3 、 Hotspot根据成本计算决定是否需要执行 CMS GC ; 可通过 -XX:+UseCMSInitiatingOccupancyOnly 来去掉这个动态执行的策略。

4 、外部调用了 System.gc ,且设置了 ExplicitGCInvokesConcurrent ; 需要注意,在 JDK 6 中,在这种情况下如应用同时使用了 NIO ,可能会出现

bug。

Page 47: Sun jdk-1.6-gc

旧生代可用 GC— 并发public class CMSGCOccur{

public static void main(String[] args) throws Exception{ byte[] bytes=new byte[1024*1024*2]; byte[] bytes1=new byte[1024*1024*2]; byte[] bytes2=new byte[1024*1024*2]; byte[] bytes3=new byte[1024*1024*1]; byte[] bytes4=new byte[1024*1024*2]; Thread.sleep(5000); }

}

-Xms20M –Xmx20M –Xmn10M -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintGCDetails

-Xms20M –Xmx20M –Xmn10M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails

Page 48: Sun jdk-1.6-gc

旧生代可用 GC— 并发1. Promotion Failed minor GC 了, to space 空间不够,往 old跑, old 也满了, so..解决方法:增大 to space ,增大 old ,或降低 cms gc 触发时机

2. Concurrent mode failure old 要分配内存了,但 old 空间不够,此时 cms gc正在进行, so..

解决方法:增大 old ,降低 cms gc 触发的 old 所占比率。

在这两种情况下,为了安全, JVM 转为触发 Full GC 。

Page 49: Sun jdk-1.6-gc

[GC [1 CMS-initial-mark: 13433K(20480K)] 14465K(29696K), 0.0001830 secs] [Times: user=0.00 sys=0.00, real=0.00 secs][CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] CMS: abort preclean due to time [CMS-concurrent-abortable-preclean: 0.007/5.042 secs] [Times: user=0.00 sys=0.00, real=5.04 secs] [GC[YG occupancy: 3300 K (9216 K)][Rescan (parallel) , 0.0002740 secs][weak refs processing, 0.0000090 secs] [1 CMS-remark: 13433K(20480K)] 16734K(29696K), 0.0003710 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

当在启动参数上设置了 -XX:+UseAdaptiveSizePolicy 后,上面的日志中的 CMS 会变为 ASCMS

旧生代可用 GC— 并发

CMS GC Log解读

Page 50: Sun jdk-1.6-gc

GC— 默认组合

新生代 GC 方式 旧生代和持久代 GC 方式

Client 串行 GC 串行 GC

Server 并行回收 GC 并行 MSC GC ( JDK 5.0 Update 6 以后)

Page 51: Sun jdk-1.6-gc

GC— 组合使用新生代 GC 方式 旧生代和持久代 GC 方式

-XX:+UseSerialGC 串行 GC 串行 GC

-XX:+UseParallelGC PS GC 并行 MSC GC

-XX:+UseConcMarkSweepGC ParNew GC 并发 GC

当出现 concurrent Mode

failure 时采用串行 GC

-XX:+UseParNewGC 并行 GC 串行 GC

-XX:+UseParallelOldGC PS GC 并行 Compacting GC

-XX:+UseConcMarkSweepGC

-XX:-UseParNewGC串行 GC 并发 GC

当出现 Concurrent Mode

failure 或 promotion failed

时则采用串行 GC

不支持的组合方式 1 、 -XX:+UseParNewGC –XX:+UseParallelOldGC

2 、 -XX:+UseParNewGC –XX:+UseSerialGC

Page 52: Sun jdk-1.6-gc

GC 方式 常用参数( -Xms –Xmx –Xmn –XX:PermSize –XX:MaxPermSize )

新生代可用 GC 串行 GC -XX:SurvivorRatio ,默认为 8 ,代表 eden:survivor ;

-XX:MaxTenuringThreshold ,默认为 15 ,代表对象在新生代经历多少次 minor gc 后才晋升

到旧生代;PS GC

-XX:InitialSurvivorRatio ,默认为 8 ,代表 new gen:survivor ;

-XX:SurvivorRatio ,默认值对于 PS GC无效,但仍然可设置,代表 eden:survivor ;

-XX:-UseAdaptiveSizePolicy , 不允许 PS GC 动态调 整 eden 、 s0 、 s1 的 大 小 , 此 时 -

XX:MaxTenuringThreshold 也可使用;

-XX:ParallelGCThreads ,设置并行 GC 的线程数。ParNew GC 同串行。

旧生代和持久代可用 GC 串行 GC 无特殊参数。

并行 GC

( 包 括

MSC 、

Compacting )

-XX:ParallelGCThreads ,设置并行 GC 的线程数。

-XX:+ScavengeBeforeFullGC , Full GC 前触发 Minor GC

并发 GC -XX:ParallelCMSThreads ,设置并发 CMS GC 时的线程数;

-XX:CMSInitiatingOccupancyFraction ,当旧生代使用比率占到多少百分比时触发 CMS GC ;

-XX:+UseCMSInitiatingOccupancyOnly ,默认为 false ,代表允许 hotspot根据成本来决定什

么时候执行 CMS GC ;

-XX:+UseCMSCompactAtFullCollection ,当 Full GC 时执行压缩;

-XX:CMSMaxAbortablePrecleanTime=5000 ,设置 preclean步骤的超时时间,单位为毫秒;

-XX:+CMSClassUnloadingEnabled , Perm Gen 采用 CMS GC 回收。

Page 53: Sun jdk-1.6-gc

JVM GC— 可见的未来

Garbage First

超出范围,以后再扯

Page 54: Sun jdk-1.6-gc

通常问题查找OOM 怎么办?

GC 怎么监测?

谁耗了内存?

Page 55: Sun jdk-1.6-gc

OOM (一些代码造成OOM的例子)Java Heap OOM产生的原因是在多次 gc 后仍然分配不了,具体策略取决于这三个参数:-XX:+UseGCOverheadLimit -XX:GCTimeLimit=98 –XX:GCHeapFreeLimit=2

1 、 OOM 的前兆通常体现在每次 Full GC 后旧生代的消耗呈不断上涨趋势; 查看方法: jstat –gcutil [pid] [intervel] [count]

2 、解决方法 dump 多次 Full GC 后的内存消耗状况,方法: jmap –dump:format=b,file=[filename] [pid] dump 下来的文件可用 MAT进行分析,简单视图分析: MAT Top Consumers

或在启动参数上增加: -XX:+HeapDumpOnOutOfMemoryError ,当 OOM时会在工作路径(或通过 -XX:HeapDumpPath 来指定路径)下生成java_[pid].hprof文件。

还有 Native Heap造成的 OOM ,堆外内存使用过多。

Page 56: Sun jdk-1.6-gc

GC 监测1 、 jstat –gcutil [pid] [intervel] [count]

2 、 -verbose:gc // 可以辅助输出一些详细的 GC信息 -XX:+PrintGCDetails // 输出 GC详细信息 -XX:+PrintGCApplicationStoppedTime // 输出 GC造成应用暂停的时间 -XX:+PrintGCDateStamps // GC 发生的时间信息 -XX:+PrintHeapAtGC // 在 GC 前后输出堆中各个区域的大小 -Xloggc:[file] // 将 GC信息输出到单独的文件中

gc 的日志拿下来后可使用 GCLogViewer或 gchisto进行分析。

3 、图形化的情况下可直接用 jvisualvm 进行分析。

Page 57: Sun jdk-1.6-gc

谁耗了内存

1 、对于长期消耗的,好办,直接 dump , MAT 就一目了然了;

2 、对于短期消耗的,比较麻烦,非图形界面暂时还没有什么好办法,图形界面情况下可使用 jvisualvm 的 memory profiler 或 jprofiler 。

Page 58: Sun jdk-1.6-gc

Tuning

如何做

Page 59: Sun jdk-1.6-gc

case 1 – 场景4 cpu , os: linux 2.6.18 32 bit启动参数

◦-Xms1536M –Xmx1536M –Xmn500M系统响应速度大概为 100ms ;当系统 QPS增长到 40 时,机器每隔 5秒就执行

一次 minor gc ,每隔 3 分钟就执行一次 full gc ,并且很快就一直 full 了;

每次 Full gc 后旧生代大概会消耗 400M ,有点多了。

Page 60: Sun jdk-1.6-gc

case 1 – 目标和方法减少 Full GC 次数,以避免由于 GC造成频繁的

长暂停,从而导致难以支撑高并发量。

方法◦降低响应时间或请求次数,这个需要重构,比较麻烦;◦减少旧生代内存的消耗,比较靠谱;◦减少每次请求的内存的消耗,貌似比较靠谱;◦降低 GC造成的应用暂停的时间。

Page 61: Sun jdk-1.6-gc

case 1 – tuning

减少旧生代内存的消耗◦jmap dump ;◦发现除了一些确实需要在旧生代中消耗的内存外,还有

点诡异现象;

可以肯定的是这里面的线程大部分是没有在处理任务的;

于是根据MAT 查找到底谁消耗掉了这些内存;◦发现是由于有一个地方使用到了 ThreadLocal ,但在使用完毕后

没有去将 ThreadLocal.set(null) 。

Page 62: Sun jdk-1.6-gc

case 1 – tuning

在做完上面的 tuning 后,旧生代的内存使用下降了大概 200M ,效果是 full gc 的频率稍微拖长了一点,但仍然不理想,于是旧生代部分无法继续优化了;

想减少每次请求所分配的内存,碰到的巨大问题:◦怎么才知道呢?貌似没办法◦想了一些方法,放弃了。

Page 63: Sun jdk-1.6-gc

case 1 – tuning

降低 GC 所造成的长暂停◦采用 CMS GC◦QPS 只能提升到 50…

◦于是放弃,而且还有和 jmap –heap 的冲突。

Page 64: Sun jdk-1.6-gc

case 1 – tuning

终极必杀技◦降低系统响应时间

QPS终于能支撑到 90…

Page 65: Sun jdk-1.6-gc

case 2 – 场景 4 cpu , os: linux 2.6.18 32 bit 启动参数

◦ -server -Xms1536m -Xmx1536m –Xmn700m 在系统运行到 67919.837秒时发生了一次 Full GC ,日志信息如

下:67919.817: [GC [PSYoungGen: 588706K->70592K(616832K)]

1408209K->906379K(1472896K), 0.0197090 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]

67919.837: [Full GC [PSYoungGen: 70592K->0K(616832K)] [PSOldGen: 835787K->375316K(856064K)] 906379K->375316K(1472896K) [PSPermGen: 64826K->64826K(98304K)], 0.5478600 secs] [Times: user=0.55 sys=0.00, real=0.55 secs]

Page 66: Sun jdk-1.6-gc

case 2 – 场景之后 minor gc 的信息如下

Page 67: Sun jdk-1.6-gc

case 2 – 场景在 68132.893 时又发生了一次 Full GC ,日志信息如下:

◦ 68132.862: [GC [PSYoungGen: 594736K->63715K(609920K)] 1401225K->891090K(1465984K), 0.0309810 secs] [Times: user=0.06 sys=0.01, real=0.04 secs]

◦ 68132.893: [Full GC [PSYoungGen: 63715K->0K(609920K)] [PSOldGen: 827375K->368026K(856064K)] 891090K->368026K(1465984K) [PSPermGen: 64869K->64690K(98304K)], 0.5341070 secs] [Times: user=0.53 sys=0.00, real=0.53 secs]

之后的时间的 GC 基本也在重复上述过程。

Page 68: Sun jdk-1.6-gc

case 2 – 目标和方法目标

◦降低 full gc 的频率,以及 gc 所造成的应用暂停;◦如果在达到上面目标的情况下,还能降低minor gc 的频率以及所造成的应用暂停时间就好了。

方法◦降低响应时间或请求次数,比较麻烦;◦减少每次请求所需分配的内存,貌似比较麻烦;◦减少每次 minor gc 晋升到 old 的对象,比较靠谱。

Page 69: Sun jdk-1.6-gc

case 2 – tuning

减少每次 minor gc 晋升到 old 的对象◦调大 new ,在当前的参数下不好操作;◦调大 Survivor ,根据目前的状况,是可选的方法;◦调大 TenuringThreshold ,根据目前的状况,这不是关键点。

Page 70: Sun jdk-1.6-gc

case 2 – tuning

调大 Survivor◦当前为 PS GC 方式, Survivor space 会被动态调整,有些

时候会调整的很小,所以导致了经常有对象直接跳到了 old ;◦于是不让动态调整了, -XX:-UseAdaptiveSizePolicy◦计算 Survivor Space 需要的大小,简单的计算了下

看目前的 to space 的大小,然后 minor gc 后晋升到 old的, old+to space 的大小作为需要的大小;

统计多次后做平均;◦于是调整…◦继续观察,并做微调,保证高峰期以及一定的冗余。◦做过这个调整后,效果很明显, minor gc 更频繁了些,但

full 的频率推迟到了至少两小时一次。

Page 71: Sun jdk-1.6-gc

case 2 – tuning上面的 tuning达到了降低 full gc 的目标,但整体GC 所造成的响应时间下降的仍然不够多,大概只下降了 10% ;

于是保持 Survivor space ,同时将 GC 方式切换为 CMS GC 。◦ -Xms1536m -Xmx1536m -Xmn700m -XX:SurvivorRatio=7

-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSMaxAbortablePrecleanTime=1000 -XX:+CMSClassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:+DisableExplicitGC

Page 72: Sun jdk-1.6-gc

GC Tuning

1 、评估现状

2 、设定目标

3 、尝试调优4 、衡量调优

5 、细微调整

Page 73: Sun jdk-1.6-gc

GC Tuning— 衡量现状1. 衡量工具

-XX:+PrintGCDetails –XX:+PrintGCApplicationStoppedTime-Xloggc: {文件名 } –XX:+PrintGCTimeStampsjmap (由于每个版本 jvm 的默认值可能会有改变,建议还是用 jmap首先观察下目前每个代的内存大小、 GC 方式)jstat 、 jvisualvm 、 sar 、 gclogviewer 系统运行状况的监测工具

2. 应收集到的信息minor gc 多久执行一次, full gc 多久执行一次,每次耗时多少?高峰期什么状况?minor gc 时回收的效果如何, survivor 的消耗状况如何,每次有多少对象会进入 old?old 区在 full gc 后会消耗多少(简单的 memory leak判断方法)系统的 load 、 cpu消耗、 qps or tps 、响应时间

Page 74: Sun jdk-1.6-gc

GC Tuning— 设定目标调优的目标是什么呢

降低 Full GC 执行频率?

降低 Full GC消耗时间?

降低 Full GC 所造成的应用暂停时间?

降低Minor GC 执行频率?

降低Minor GC消耗时间?

例如某系统的 GC 调优目标:降低 Full GC 执行频率的同时,尽可能降低minor GC 的执行频率、消耗时间以及 GC 对应用造成的暂停时间。

Page 75: Sun jdk-1.6-gc

GC Tuning— 尝试调优根据目标针对性的寻找瓶颈以及制定调优策略

来说说常见的

降低 Full GC 执行频率 根据前面学习到的 Full GC 触发时机,寻找到瓶颈 为什么 Full GC 执行频率高呢, old 经常满?还是 old 本来占用就高呢? old 为什么经常满呢?请参见 PPT 前面的内容 ... 是不是因为 minor gc 后经常有对象进入 old呢?为什么?

注意 Java RMI 的定时 GC 触发,可通过:-XX:+DisableExplicitGC 来禁止;或通过 -Dsun.rmi.dgc.server.gcInterval=3600000 来控制触发的时间。

Page 76: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低 Full GC 执行频率 – 通常瓶颈

Old 本身占用的就一直高,所以只要稍微放点对象到 old ,就 full 了; 通常原因:缓存的东西太多 oracle 10g驱动时 preparedstatement cache太大 查找办法,很简单: dump then mat,bingo!

Page 77: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低 Full GC 执行频率 – 通常瓶颈

Minor GC 后总是有对象不断的进入 Old ,导致 Old 不断的满 通常原因: Survivor太小了 系统响应太慢、请求量太大、每次请求分配内存多 分配的对象太大 ... 查找办法: dump 这时就不是很好用了,需要的是能分析两次 minor GC 之间到底哪些地方分配了内存; jstat观察 Survivor 的消耗状况, -XX:PrintHeapAtGC 输出 GC 前后的详细信息; 系统响应慢那行属于系统优化,不在这里扯;

Page 78: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低 Full GC 执行频率 – 调优策略

Old 本身占用的就一直高 调优办法 ① 扩大 old 大小(减少 new 或调大 heap );

减少 new 注意对 minor gc 的影响并且同时有可能造成 full gc还是严重; 调大 heap 注意 full gc 的时间的延长, cpu 够强悍嘛, os 32 bit 的吗?

② 程序优化(去掉一些不必要的缓存)

Page 79: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低 Full GC 执行频率 – 调优策略

Minor GC 后总是有对象不断的进入 Old 前提:这些进入 Old 的对象在 full 时大部分都会被回收 调优办法 ① 降低Minor GC 执行频率(等到那部分再讲) ② 让对象尽量在 Minor GC 中就被回收掉 放大 new 、放大 survivor 、增大 TenuringThreshold 但要注意这些有可能会造成minor gc 执行频繁

③ 换 CMS GC Old还没满就回收掉,从而降低 Full GC 触发的可能

④ 程序优化 提升响应速度、降低每次请求分配的内存

Page 80: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低单次 Full GC 执行时间

通常原因: 旧生代太大了 ... 通常办法: 是并行 GC吗? 加点 CPU吧 升级 CPU吧 减小 Heap 或旧生代吧

Page 81: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低Minor GC 执行频率

通常原因: 每次请求分配的内存多、请求量大 通常办法: 扩大 heap 、扩大新生代、扩大 eden ,扩大时请综合考虑; 降低每次请求分配的内存; 加机器吧,分担点请求量。

Page 82: Sun jdk-1.6-gc

GC Tuning— 尝试调优 降低Minor GC 执行时间

通常原因: 新生代太大了 响应速度太慢了,导致每次 Minor GC 时存活的对象多 通常办法: 减小点新生代吧; 加点 CPU吧、升级 CPU吧; 响应能不能快点呢?

Page 83: Sun jdk-1.6-gc

GC Tuning— 算命

① 当响应速度下降到多少、或请 求量上涨到多少时,系统会 挂掉?

② 参数调整后系统多久会执行一次 Minor ,多久会执行一次Full ,高峰期会如何?

Page 84: Sun jdk-1.6-gc

GC Tuning— 不是瞎算的① 系统的生辰八字

每次请求平均需要分配多少内存? 系统的平均响应时间是多少呢? 请求量是多少、多久一次 Minor 、 Full?

② 先掐指算下 在现在的参数下,应该是多久一次 Minor 、 Full ,对比真实状况,做一定 的偏差;

③ 根据所掌握的知识,就可以判断了

Page 85: Sun jdk-1.6-gc

GC Tuning— 来算一卦

Page 86: Sun jdk-1.6-gc

GC Tuning— 总结

是个复杂过程,按照 Tony ( GC主要作者的

说法): GC Tuning is art!

综合考虑,每个参数的调整都有可能带来其他的影响。

总结来说,提升响应速度、降低每次请求分配的内存才是必杀技!或者不计成本: 64 bit ,多个高档 CPU 、大量的内存都上!

Page 87: Sun jdk-1.6-gc

实现Hotspot GC 是如何实现的

Page 88: Sun jdk-1.6-gc

内存回收1 、通常有两种实现方法:

1.1 引用计数 不适合复杂对象引用关系,尤其是有循环依赖的场景; 需要计数; 优点是只要计数器降为 0 ,就可被回收。 1.2 跟踪(有向图 Tracing ) 适合于复杂对象引用关系场景, Hotspot 采用这种; 需要到达某个时机后触发执行,并且通常需要暂停应用线程。 常用算法: Copying 、 Mark-Sweep 、 Mark-Compact ,算法请

参见《垃圾回收》这本绝版书。

Page 89: Sun jdk-1.6-gc

内存回收Hotspot 从 root set 开始扫描有引用的对象,并对 Reference 类型的对象特殊处理。

root set1 、当前正在执行的线程;2 、全局 /静态变量;3 、 JVM Handles ;4 、 JNI Handles ;

Page 90: Sun jdk-1.6-gc

内存回收如何暂停应用线程?

safepoint first: 检测某内存页是否可读的指令

先想想:只有会改变引用关系的地方才需要暂停,否则没必要,因此对于 正在 native 中执行的线程, JVM 是不管的,而当 native 代码需要改变引用 关系时,又回到了 hotspot java 部分的代码,而在那些代码上是会有 safepoint 的。

代码编译时会在某些部分生成 safepoint ,当需要 GC 时, GC 会向 core vm提交暂停应用线程的请求, core vm 会将 safepoint 检测的内存页置为不可读状态,当解释执行或 JIT编译执行到 safepoint 时,发现内存页不可读,于是就挂起了。

Page 91: Sun jdk-1.6-gc

串行 GC(Serial

Copying)

并行回收 GC(Parallel

Scavenge)

并行 GC(ParNew)

新生代可用 GC

Page 92: Sun jdk-1.6-gc

在分配时均采用连续空间和 bump the pointer 的方式。

A B C D

新生代可用 GC

Page 93: Sun jdk-1.6-gc

在回收时均采用如下策略: 扫描新生代,找出其中 活的对象; 基于 Copying 算法回收, 新生代中活的对象在回收 时会根据一定的规则晋升 到旧生代。

新生代可用 GC

Page 94: Sun jdk-1.6-gc

新生代可用 GC由于只扫描新生代,如旧生代的对象引用了新生代的,怎么办?

在给对象赋引用时,会经过一个 write barrier ;

检查是否为旧生代引用新生代,如为则记录到 remember set 中;

在 minor gc 时, remember set 指向的新生代对象也作为 root set 。

Page 95: Sun jdk-1.6-gc

新生代可用 GC— 串行完整内存分配策略1 、首先在 tlab 上尝试分配;2 、检查是否需要在 new 上分配,如需要分配的大小小于

PretenureSizeThreshold ,则在 eden 上进行分配,分配成功则返回, 分配失败则继续;

3 、检查是否需要尝试在旧生代上分配,如需要,则遍历所有代,并检查是 否可在该代上分配,如可以则进行分配;如不需要在旧生代上尝试分配, 那么则检查是否可以在 eden 上分配,如可以则分配, 否则则尝试在 old 上分配;

4 、根据策略决定执行新生代 GC 或 Full GC ,执行 full gc 时不清除 soft Ref ;5 、如需要分配的大小大于 PretenureSizeThreshold ,尝试在 old 上分配,

否则尝试在 eden 上分配;6 、尝试扩大堆并分配;7 、执行 full gc ,并清除所有 soft Ref ,按步骤 5 继续尝试分配。

Page 96: Sun jdk-1.6-gc

新生代可用 GC— 串行完整内存回收策略

1 、检查 to 是否为空,不为空返回 false ;2 、检查 old 剩余空间是否大于当前 eden+from 已用的大小,如大于则返回

true ,如小于且 HandlePromotionFailure 为 true ,则检查剩余空间是否大于之前每次 minor gc 晋级到 old 的平均大小,如大于返回 true ,如小于返回false 。

3 、如上面的结果为 false ,则执行 full gc ,如上面的结果为 true ,执行下面的步骤;

4 、扫描引用关系,将活的对象 copy 到 to space ,如对象在 minor gc 中的存活次数超过 tenuring_threshold 或分配失败,则往旧生代复制,如仍然复制失败,则取决于 HandlePromotionFailure ,如不需要处理,直接抛出 OOM ,并退出 vm ,如需处理,则保持这些新生代对象不动;

Page 97: Sun jdk-1.6-gc

新生代可用 GC—ParNew内存分配策略和串行方式完全相同。

Page 98: Sun jdk-1.6-gc

新生代可用 GC—ParNew内存回收策略

策略和串行相同,只是回收转为了多线程方式。

Page 99: Sun jdk-1.6-gc

新生代可用 GC—PS完整内存分配策略

1 、先在 TLAB 上分配,分配失败则直接在 eden 上分配; 2 、当 eden 上分配失败时,检查需要分配的大小是否 >= eden space 的一半,如是,则直接在旧生代分配; 3 、如分配仍然失败,且 gc 已超过频率,则抛出 OOM ; 4 、进入基本分配策略失败的模式; 5 、执行 PS GC ,在 eden 上分配; 6 、执行非最大压缩的 full gc ,在 eden 上分配; 7 、在旧生代上分配; 8 、执行最大压缩 full gc ,在 eden 上分配; 9 、在旧生代上分配; 10 、如还失败,回到 2 。

最悲惨的情况,分配触发多次 PS GC 和多次 Full GC ,直到OOM 。

Page 100: Sun jdk-1.6-gc

新生代可用 GC—PS完整内存回收策略

1 、如 gc 所执行的时间超过,直接结束;2 、先调用 invoke_nopolicy 2.1 先检查是不是要尝试 scavenge ; 2.1.1 to space必须为空,如不为空,则返回 false ; 2.1.2 获取之前所有 minor gc 晋级到 old 的平均大小,并对比目前

eden+from 已使用的大小,取更小的一个值,如 old 剩余空间 小于此值,则返回 false ,如大于则返回 true ; 2.2 如不需要尝试 scavenge ,则返回 false ,否则继续; 2.3 多线程扫描活的对象,并基于 copying 算法回收,回收时相应的晋升对象到旧生代; 2.4 如 UseAdaptiveSizePolicy ,那么重新计算 to space 和

tenuringThreshold 的值,并调整。3 、如 invoke_nopolicy返回的是 false ,或之前所有 minor gc 晋级到 old 的

平均大小 > 旧生代的剩余空间,那么继续下面的步骤,否则结束;4 、如 UseParallelOldGC ,则执行 PSParallelCompact ,如不是

UseParallelOldGC ,则执行 PSMarkSweep 。

Page 101: Sun jdk-1.6-gc

串行 GC(Serial MSC)

并行 MSC GC(Parallel MSC)

旧生代可用的 GC

并发 GC(CMS)

并行 Compacting GC(Parallel Compacting)

Page 102: Sun jdk-1.6-gc

旧生代可用 GC— 串行内存分配策略

1 、不支持 TLAB ;

2 、 bump pointer 的分配方式。

Page 103: Sun jdk-1.6-gc

旧生代可用 GC— 串行内存回收策略

1 、基于 Mark Sweep Compact 实现;2 、如 CollectGen0First 为 true (默认为 false ),则先执行 minor GC ;

回收过程分为四个阶段完成:1 、标记哪些对象是活的;2 、计算新的地址;3 、更新指针指向新的地址;4 、将对象移到新的地址。

Page 104: Sun jdk-1.6-gc

旧生代可用 GC— 并行 MSC内存分配策略

1 、在旧生代上按照 bump pointer机制进行分配;

2 、分配不了的情况下尝试等待几毫秒(通过以下参数设置)后再分配; GCExpandToAllocateDelayMillis ,默认为 0

3 、再分配不了就返回 NULL 。

Page 105: Sun jdk-1.6-gc

旧生代可用 GC— 并行 MSC内存回收基于 Mark Sweep Compact 实现。

四个阶段:1 、标记活的对象;2 、计算这些活的对象新的目标地址;3 、更新指针指向新的地址;4 、移动对象到新的地址。

Page 106: Sun jdk-1.6-gc

旧生代可用 GC— 并行 Compacting

内存分配策略和并行 MS 相同。

Page 107: Sun jdk-1.6-gc

旧生代可用 GC— 并行 Compacting

内存回收基于 Mark Compact 实现,图示如下:

Page 108: Sun jdk-1.6-gc

旧生代可用 GC— 并发内存分配策略

1 、首先要拿到 freelist锁;2 、找到可以容纳下对象大小的 chunk ,然后分配;3 、如目前正在 marking阶段,那么将此新分配的对象标识为活的。

Page 109: Sun jdk-1.6-gc

旧生代可用 GC— 并发1. 系统启动时在后台启动一个 CMS 线程,定时检查是否需要触发 CMS GC

2. 基于 Mark-Sweep 实现; Initial Marking ( Stop-the-world )

Concurrent MarkingPreClean ( Sun HotSpot 1.5 后引入的优化步骤)Final Marking ( Stop-the-world )

Concurrent Sweeping

Page 110: Sun jdk-1.6-gc

旧生代可用 GC— 并发1. Initial Marking ( Stop-the-world )

mark 下 root set 直接引用的对象

2. Concurrent Marking 并发标识上面 mark 出来的对象的引用; Mod Union Table

Minor GC 同时进行,有可能会导致旧生代引用的对象关系改变 Card Table 旧生代中的对象引用关系也有可能改变

Page 111: Sun jdk-1.6-gc

旧生代可用 GC— 并发3. Preclean 重新扫描上一步过程中新创建的对象和引用关系改变了的对象的引

用关系; 此步什么时候执行,以及执行到什么时候再触发后续动作,取决于 两个值: -XX: CMSScheduleRemarkEdenSizeThreshold 、 -XX: CMSScheduleRemarkEdenPenetration 第一个值默认为 2 ,第二个值默认为 50% ,代表着当 eden space 使用超过 2M 时,执行此步,当使用超过 50% 时,触发 remark ; 上面这个步骤有些时候有可能会引发 bug ,有对象需要在 old 分配 空间,但由于 remark总是没执行,导致 old 空间不足,默认此步的 超时时间为 5秒,可通过 -XX: CMSMaxAbortablePrecleanTime 设置,单位为毫秒。

Page 112: Sun jdk-1.6-gc

旧生代可用 GC— 并发4. Final Marking ( Stop-the-world )

处理 Mod Union Table 和 Card Table 中 dirty 的对象,重新 mark 。

5. Concurrent Sweeping并发回收。

Page 113: Sun jdk-1.6-gc

旧生代可用 GC— 并发优缺点

1. 大部分时候和应用并发进行,因此只会造成很短的暂停时间;

2. 浮动垃圾,没办法, so 内存空间要稍微大一点;

3. 内存碎片, -XX:+UseCMSCompactAtFullCollection 来解决;

4. 争抢 CPU ,这 GC 方式就这样;

5. 多次 remark ,所以总的 gc 时间会比并行的长;

6. 内存分配, free list 方式, so性能稍差,对 minor GC 会有一点影响;

7. 和应用并发,有可能分配和回收同时,产生竞争,引入了锁, JVM 分配优先。

Page 114: Sun jdk-1.6-gc

旧生代可用 GC— 并发浮动垃圾 产生于 card table