More Related Content Similar to Jvm内存管理基础 (20) More from wang hongjiang (9) Jvm内存管理基础2. 说明
► 课程目的
帮助 Java 程序员更好的认识 JVM ,掌握 GC 基础知识,了解现有线上业务的 GC 配置及问
题
► 大纲
1: JVM 内存结构
从对象的创建和消亡过程看内存
2: GC 的选择
了解各种 GC 的特点,选择的依据
3: CMS GC 详情
详细了解 CMS GC 的特点
4: 当前业务线线上的 GC 配置
参考集团各子公司业务线的 GC 配置以及问题
► 学习对象要求
Java 程序员 <= P6
► 联系我: http://laiwang.com/u/3001
4. JVM 运行时数据区
heap non-heap
Native
VM Hotspot 里
Method
Stack 本地方法栈
Stack
堆空间,不同的 与 java 栈在
GC 策略有不同的 Program counter 一起
划分方式 register
Method Area
5. 类的初始化
► 引子,初始化问题
public class TestA{
static { a = 1; }
static int a = 0; // 1) 如果不赋值呢? 2) 如果这一句与上一句位置互换?
public static void main(String[] args){
System.out.println(TestA.a);
}
}
8. 类的初始化:生命周期
展开其中的 Link 过程,又有三个步骤:
看另一段初始化的代码
Verify Prepare Resolve
(校验) (准备) (解析)
为类变量分配内存,设置
默认值。注意:此时在
<clinit> 之前
http://frankkieviet.blogspot.com/2009/03/javalanglinkageerror-loader-constraint.html
//FIXME
9. 类的初始化: <clinit> 的问题
► 此过程的数据主要在哪个区域?
► 看一段代码例子:
中文站曾大面积发生过的现象 ( 高并发情况
下)
基础框架里 (toolkit.common.lang) 里潜伏
的
简化后的代码:
10. class Lock {}
class Danger {
static {
System.out.println("clinit begin...");
try {Thread.sleep(2000);} catch (Exception e) {}
synchronized (Lock.class) { System.out.println("clinit done!");}
}
}
public class Test {
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized (Lock.class) {
System.out.println("new thread start!");
try {Thread.sleep(1000);} catch (Exception e) {}
new Danger();
}
System.out.println("new thread end!");
}
}.start();
try {Thread.sleep(500);} catch (Exception e) {}
System.out.println(new Danger());
System.out.println("DONE!");
}
}
11. 诊断方法
► jstack javapid > file
► printf “%0x” threadId
对工具的使用另外展开一个话题,参考:
http://www.slideshare.net/hongjiang/effective-linux2tools
http://www.slideshare.net/hongjiang/effective-linux3diagnosis
12. 类的初始化:
运行时数据区与线程安全
heap non-heap
VM
线程私有
Stack
除了在 Eden 区
每个线程分配一块
缓冲 (TLAB) ,在该区 Program counter
register 线程私有
是线程私有。
其他都是线程共享的
Perm gen 线程共享
13. 类的初始化:
运行时数据区与线程安全
► <clinit> 更新类的数据是在 perm gen 区域
► 当多个线程需要初始化一个类,仅仅运行一
个线程来进行,其他线程需要等待
(object.wait) 。当活动的线程完成初始化之
后,它必须通知其他等待线程。
14. 类的初始化: PermGen 的数据内容
Class mate data
基本类型信息 动态连接
类型信息 依赖的数
类型常量池 据
字段信息
字节码
方法信息
类成员 ( 静态变量 )
通过
classloader 引用
<clinit>
来修改
Class 类实例的引用
15. 类的初始化
► 常量池的内容 // TODO
► 怎么查看常量池中的字符串常量
// 参考 PermStat.java 的实现
class StringPrinter implements StringTable.StringVisitor
17. 类的初始化: PermGen 区域的变数
► 未来 Jdk7 的某个 update 可能将 PermGen
彻底去除, class metadata 将放入 C-heap
区域。 Hotspot 与 JRockit 整合。
► java7目前 release 的版本中,已经将字符串
常量池移到了 main heap ( 对于分代 gc 来说
是 old gen 区域 )
看一个例子:
18. 类的初始化: PermGen 区域的变数
1 ) 调用 String.intern()
scala> for(i <- 0 to 3000000)
new String("xxxxxxxxxxxxxxxxx" + i).intern
对比 jdk6 与 jdk7 的差异
2 )产生随机的大字符串做常量,对比 jdk6/7
看看存在哪儿?
20. 对象的创建
► 了解一个前提:基于逃逸分析 (Escape Analysis) 的优化:
$ jinfo –flag DoEscapeAnalysis javapid
在 server mode(c2) 下是默认开启的 (jdk6 u23 之后 )
1) Stack Allocations 栈上分配
在 jdk7 上还没有实现
也许在 jdk8 上对 closure 可以实现在栈上分配 ( 别相信我 )
2) Synchronization Elimination 同步消除
-XX:+EliminateLocks 默认已开启
3) Scalar Replacement 标量替换
-XX:+EliminateAllocations 默认已开启
21. 对象的创建: Allocations
1) 在 stack 上创建(未来可能)最高效
,避免了 GC
① Stack
② TLAB
2) 在 TLAB 区域创建,避免了竞争的代
Heap
价 ( 默认 ) ( Eden)
③
3) 在 Eden 区域创建 ( 默认 )
④ Heap
(Old)
4) 在 Old 区创建,需满足条件,比如对
象大于阈值
22. 对象的创建: Allocations
► fast allocation
1) bump the pointer
前提:有大块的连续空闲空间
内部维护一个指针( allocatedTail ),它始终指向先前已分配对象的尾部,当新的对象分
配请求到来时,只需检查代中剩余空间(从 allocatedTail 到代尾 geneTail )是否足以容纳
该对象
2) thread local allocation buffers(TLABs)
避免了多线程竞争同步的开销
在 TLABs 区域也可以采用 bump-the-pointer 方式
23. 对象的创建: TLAB
► Thread Local Allocation Buffer
https://blogs.oracle.com/jonthecollector/entry/t
https://blogs.oracle.com/jonthecollector/entry/
https://blogs.oracle.com/jonthecollector/entry/a
https://blogs.oracle.com/jonthecollector/entry/
24. 对象的创建: TLAB
► TLAB 的参数
-XX:+UseTLAB 启用 TLAB 默认开启
-XX:+ResizeTLAB 动态设置每个线程的 tlab 大小 默认开启
-XX:TLABSize=X TLAB 初始大小 默认 0
-XX:TLABWasteTargetPercent=X 占 Eden 区的百分比 默认 1%
-XX:PrintTLAB 打印 TLAB 相关信息 默认不开启
25. 对象的创建 : Eden
► 在 Eden 区分配
TLABs 未开启或无可用空间
► Eden 区空间不足时
minor gc 存活对象从 Eden 移动到
Survivor
Survivor 区空间不足则晋升到老生代
26. 对象的创建 : Tenured
► 直接分配在 old gen 的例子:
$ scala -J-Xms100m -J-Xmx100m -J-Xmn50m
-J-XX:PretenureSizeThreshold=2097152 -J-XX:+UseConcMarkSweepGC
scala> System.gc
scala> val data = new Array[Byte](10*1024*1024)
scala> System.gc
scala> println(data.length)
配合 jconsole 观察 old 区的内存变化
注意,在 Parallel Scavenge GC 下使用 PretenureSizeThreshold 无效,
Parallel Scavenge GC 的新生代采用自适应策略自动调节各区域空间
27. 指定区域大小的参数与 OOM 的可
能
heap non-heap
VM -Xss
Stack
唯一没有规
定 OOM 的区
Program counter
-Xms 域
register
-Xmx -XX:PermSize
-Xmn -XX:MaxPermSize
Perm gen
JIT 缓存
Code Cache -XX:
ReservedCodeC
acheSize
28. Stack 区的几点吐槽
1) -Xss 和 -XX:ThreadStackSize 是一回事,– Xss 是面向所有 JVM ,后
者是 hotspot 专用, hotspot 会把 Xss 转换为 ThreadStackSize
3) 查看 java 进程的栈大小时通过 jinfo –flag ThreadStackSize javapid
5) 在不同的 OS 和架构 (32/64)Xss 默认值也是不同的, 64 位 linux 上默
认是 1024k (1m) ,在我们参数中有些还设置的 -Xss=256k 是早期设置
的,现在已经不太合理
7) 在 64 位 jvm ,设置 Xss 时提示最少要 104k
9) Xss 并不是每个线程启动后都立刻分配了指定大小的栈空间的,否则
jvm 中若有 1000 个线程,按默认每个 1m ,仅线程栈就会占用 1G 内
存。
29. 对象的消亡
► 前提,了解引用类型
强引用 ( 默认 )
软引用 (soft reference)
弱引用 (weak reference)
幽灵引用 (phantom reference)
► 为什么引入另外三种 Reference ?
程序与 GC 有了交互方式
30. 对象的消亡: reference
reachable
强可达 strong reachable
线程根引用
强引用
C
A
强引用
软引用
B
B 不会被 GC 回收
31. 对象的消亡: reference
reachable
软可达 soft reachable
线程根引用
软引用 软引用
C
A
软引用
D
软引用
强引用 B
内存不足时, B 会被 GC 回收
SoftRef 的存活时间 /M 空闲空间: -XX:SoftRefLRUPolicyMSPerMB 默认
1秒
32. 对象的消亡: reference
reachable
弱可达 weak reachable
线程根引用
弱引用 弱引用
C
A
弱引用
D
弱引用
强引用 B
GC 触发时 B 会被回收
33. 对象的消亡: ReferenceQueue
监听器
eg1 : 对 FinalReference 类型
ref 计数,执行其 finalize
方法
pending 链表
ReferenceQueue
GC next pending ReferenceQueue
ReferenceQueue
discovered ref
ReferenceHandler
线程负责轮询 pending 链表,
根据 queue 的类型决定是否将
元素放入 queue
34. ReferenceQueue 的案例
► 从这篇帖子说起
“WeakHashMap 的神话” : http://www.javaeye.com/topic/587995
结合 WeakHashMap 的代码,分析一下作者的问题
35. 对象的消亡: finalize
1) JVM 中存在的两个 daemon 线程
java.lang.ref.Finalizer$FinalizerThread 消费者(处理出队的元素)
java.lang.ref.Reference$ReferenceHandler 生产者(入队)
2) 对象实现 finalize 方法需要 2 次 GC 才能回收
所有实现了 finalize 方法的对象会被封装为 FinalReference ,第一次 gc 时被放入
ReferenceQueue 确保 finalize 方法被执行,第二次 gc 才真正被收集
jmap -finalizerinfo javapid
显示在 F-Queue 中等待 Finalizer 线程执行 finalize 方法的对象。
3) finalize 导致对象复活
幽灵引用的用途
36. 类的消亡:卸载 Class
1) 系统 classloader 加载的 Class 不会被卸载
只有非系统 classloader 加载的 Class 才有可能
被卸载。
2) Class 若有 static field 或 static block ,卸载
时除了没有可达引用,还需要其 classloader
也没有被使用才行。 Why ?
假设可以被卸载,当需要重新加载时, static field 被重新初始化,其
状态可能与之前不一致; static 构造块也会重新执行,与预期不一致,
或许引起副作用。
37. 类的消亡: Perm Gen 的泄漏
► Classloader 泄漏
Classloader1 Classloader2
C.class D.class A.class
实例 B.class
a
用于动态加载的 classloader1 是 classloader2 的 child , A 实例化时注册到了 B 静态成员中,形成上面的情
况, classloader1 及其加载的所有类都不会被卸载
参考此例: http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html
39. GC 的选择 (G1 除外 )
► -XX:+UseSerialGC
采用 Serial + Serial Old 组合 , client 模式默认是此方式
► -XX:+UseParallelGC
采用 Parallel Scavenge + Serial Old(PS MarkSweep) 组合, server 模式默认
► -XX:+UseParNewGC
采用 ParNew + Serial Old 组合
► -XX:+UseParallelOldGC
采用 Parallel Scavenge + Parallel Old 组合
► -XX:+UseConcMarkSweepGC
采用 ParNew + CMS + Serial Old(CMS 的备用 ) 组合
► -XX:+UseConcMarkSweepGC -XX:-UseParNewGC
采用 Serial + CMS + Serial Old(CMS 的备用 ) 组合
40. GC 的选择:新生代与旧生代的组合
吞吐量优先
可自适应调节
新生代各区域
Jdk1.4 提供
Parallel
Serial ParNew
Scavenge
CMS
Jdk1.5 Parallel Old
提供 Serial/
PS MarkSweep
作为 CMS 的备
份,在 CMS 并 PS 的旧生代版本
发失败时采用 Jdk1.6 提供
41. GC 的选择: -XX:+UseSerialGC
采用 Serial + Serial Old 组合 , client 模式默认是此方式
Parallel
Serial ParNew
Scavenge
Serial/
CMS Parallel Old
PS MarkSweep
42. GC 的选择: -XX:
+UseParallelGC
采用 Parallel Scavenge + Serial Old(PS MarkSweep) 组合,
server 模式默认
Parallel
Serial ParNew
Scavenge
Serial/
CMS Parallel Old
PS MarkSweep
43. GC 的选择: -XX:
+UseParNewGC
采用 ParNew + Serial Old 组合
Parallel
Serial ParNew
Scavenge
Serial/
CMS Parallel Old
PS MarkSweep
45. GC 的选择: -XX:
+UseConcMarkSweepGC
采用 ParNew + CMS + Serial Old(CMS 的备用 ) 组
合
Parallel
Serial ParNew
Scavenge
CMS Serial/ Parallel Old
备用 PS MarkSweep
46. GC 的选择: -XX:
+UseConcMarkSweepGC
-XX:- UseParNewGC
采用 Serial+ CMS + Serial Old(CMS 的备用 ) 组合
Parallel
Serial ParNew
Scavenge
CMS Serial/ Parallel Old
备用 PS MarkSweep
47. GC 的选择:策略依据
► 从应用场景:
1) web app ,用户响应优先;
2) 任务,吞吐率优先
► 从成熟度和稳定性
生产环境选择一种策略前需要大量测试
48. Minor GC
► young generation
eden S0 S1
1) Survivor 区大小可通过 -XX:SurvivorRatio 来设置 , CMS GC 默认为 8
即 -Xmn=500m 的话, eden 占 80% 为 400m , s0 和 s1 各 10% 为
50m
2) 可通过 – XX:MaxTenuringThreshold=X 设置对象经历多少次
minor gc 才进入旧生代,并行 GC 默认 15 , CMS GC 默认为 4
3) 为什么采用 2 个 Survivor 区域?因为该区 gc 主要采用拷贝算法
49. Minor GC , copying 算法
from to
[admin@exodus-web2244 ~]$ jstat -gcutil 22063 1000
S0 S1 E O P YGC YGCT FGC FGCT GCT
21.30 0.00 3.29 34.34 68.37 71190 1450.721 20 1.451 1452.173
21.30 0.00 32.18 34.34 68.37 71190 1450.721 20 1.451 1452.173
21.30 0.00 64.86 34.34 68.37 71190 1450.721 20 1.451 1452.173
0.00 18.42 6.02 34.35 68.37 71191 1450.751 20 1.451 1452.202
0.00 18.42 41.07 34.35 68.37 71191 1450.751 20 1.451 1452.202
0.00 18.42 68.41 34.35 68.37 71191 1450.751 20 1.451 1452.202
22.26 0.00 4.08 34.35 68.37 71192 1450.771 20 1.451 1452.222
22.26 0.00 49.14 34.35 68.37 71192 1450.771 20 1.451 1452.222
22.26 0.00 79.94 34.35 68.37 71192 1450.771 20 1.451 1452.222
to from
50. Major GC
► 只分享 CMS GC
► CMS GC 与 Full gc 是一回事么?
51. CMS GC 的详细过程
① ② ③ ④
concurrent final concurrent
用户线程 preclean
initial marking marking sweeping
marking
用户线程 用户线程
safepoint safepoint
stop-the-world stop-the-world
两次 stop-the-world ,这也是为什么 jstat –gcutil 看到 FGC 次数每次增长为 2
(current mode failure 的情况另说 )
52. CMS GC 的详细过程
① Initial marking Root Set
仅标记根集合
直接可达对象 , A1 B1 C1 D1
记录在外部的
bitmap
to-be-scanned
A3 B2
A2
A4
53. CMS GC 的详细过程
② Concurrent marking Root Set
A1 B1 C1 D1
marking 的过程即
遍历所有对象,进
行着色, A3 B2 借助 markingStack 栈完成遍历,
最费时的一个过程 A2 可通过下面参数设置其大小
-XX:CMSMarkStackSize=xxx
( 默认 32k)
-XX:CMSMarkStackSizeMax=yyy
A4 ( 默认 4M)
采用栈的方式,就有栈溢出的可能
,不过实际过程有很多技巧,未探
究
54. CMS GC 的详细过程
② Concurrent marking
此过程 minor gc 和用户线程也是可以工作的;
怎么应对 minor gc 和用户线程对数据修改不一致 ?
Heap
Card Table
Mod Union Table
划分为多个区域 (card) ,如果该区
域用户线程改变数据发生不一致 记录 minor gc
(dirty) ,则记录在 card table 中 修改的 card
55. CMS GC 的详细过程
② preclean
新增的一个优化步骤,减少下一阶段 (remark) stop-the-world 的时间
-XX:CMSMaxAbortablePrecleanTime=5000
56. CMS GC 的详细过程
③ Final marking (stop-the-world)
C D F
A G
B E
该区域对象发生
过变化 (dirty)
重新 marking
57. CMS GC 的详细过程
④ Concurrent sweeping
Sweeping 之后的碎片问题
Root A B C D E
Set
对于碎片整理,可通过 对于 mark-swap 算法的 gc 另
-XX:UseCMSCompactAtFullCollection 一个不利是由于碎片的原因不
在每次 fullgc 时整理,也可使用 好使用
-XX:CMSFullGCsBeforeCompaction=X Bump-the-pointer 方式来分配
指定多少次 fullgc 后整理 内存 ( 适合于大片连续的空间 )
58. CMS GC 详情 : 浮动垃圾
► 浮动垃圾的产生
图中的对象 c 为浮动垃圾,需要等下次 gc 才能回收。
http://research.sun.com/techrep/2000/smli_tr-2000-88.pdf
59. CMS GC 详情 : PLABs
Promotion local allocation buffers (PLABs)
在新生代,并发收集时,对象将可能从 eden 晋升到 survivor 区,或老生代。
在 survivor 或 old 区为晋升的对象分配空间时为了避免多线程竞争,对每个线程
分配了缓冲区,在 survivor 区每个线程有一个 PLAB ,在 old 区每个线程也有一个
PLAB
但在 survivor 区域,可用空间是连续的 ( 拷贝算法,总有一个 survivor 会空出
来 ) ,所以 PLAB 也是连续的,而 old 区的空间则可能是非连续的 (cms 产生的碎
片 ) ,所以 PLAB 可能通过一些手段组织一些非连续的空间
对比一下 TLABs ,意图一样,不过情况复杂一些。
-XX:+PrintOldPLAB
60. CMS GC 详情 : 触发
► CMS GC 的触发:
1: 旧生代空间使用到一定比率
-XX:CMSInitiatingOccupancyFraction=XX Jdk6 默认 92%
2: perm gen 采用 CMS 且空间使用到一定比率
perm gen 采用 CMS 收集需设置: -XX:+CMSClassUnloadingEnabled
可通过 -XX:CMSInitiatingPermOccupancyFraction 来指定
Jdk6 默认为 92%
3: System.gc ,且设置了 ExplicitGCInvokesConcurrent
4: JVM 根据情况自行判断 ( 启发式 ) 决定是否进行
cms 基于对 promotion 的统计,自行决定是否需要
可通过 -XX:+UseCMSInitiatingOccupancyOnly 禁止启发式执行 cms gc
61. CMS GC 详情 : 触发
► CMS GC 的触发 :
1: 旧生代空间使用到一定比率 (CMSInitiatingOccupancyFraction)
JDK6 默认 92%
[wei@vm-qa-cn-141-155 whj]$ jstat -gcutil 18592 2000
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 16.13 16.21 91.93 98.31 9635 127.704 0 0.000 127.704
0.00 17.43 13.38 91.94 98.31 9651 127.926 0 0.000 127.926
22.38 0.00 62.11 34.40 86.32 9668 128.144 2 0.123 128.267
0.00 22.61 57.11 34.47 86.32 9685 128.353 2 0.123 128.476
15.44 0.00 6.86 34.54 86.32 9702 128.586 2 0.123 128.708
62. CMS GC 详情 : 触发
► CMS GC 的触发 :
2: perm gen 采用 CMS 且空间使用到一定比率
$ scala -J-XX:PermSize=40M -J-XX:MaxPermSize=40M -J-XX:+UseConcMarkSweepGC -J-
Xms1g -J-Xmx1g -J-XX:+CMSClassUnloadingEnabled
scala > :cp /data/tmp/demo/
scala > :load /data/tmp/demo/import
开启 CMSClassUnloadingEnabled , perm gen 达到 92% 触发 cms gc
S0 S1 E O P YGC YGCT FGC FGCT GCT
83.19 0.00 6.62 4.28 90.86 16 0.425 0 0.000 0.425
0.00 84.85 80.56 4.45 91.74 19 0.451 0 0.000 0.451
85.31 0.00 0.00 4.72 92.17 24 0.495 3 0.048 0.543
92.21 0.00 0.00 4.95 93.03 28 0.529 6 0.127 0.656
86.89 0.00 50.94 5.18 93.91 32 0.558 10 0.171 0.728
63. CMS GC 详情 : 触发
► CMS GC 的触发 :
3: 调用 System.gc 且启用 ExplicitGCInvokesConcurrent
$ scala -J-XX:+UseConcMarkSweepGC -J-Xms1g -J-Xmx1g
-J-XX:+ExplicitGCInvokesConcurrent
scala > System.gc
注,这里仍用 jstat –gcutil 来观察, FGC=2 表示一次 CMS GC ,这不太严禁,可以
用 GC log 来观察
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 93.83 0.00 99.46 0 0.000 0 0.000 0.000
0.00 0.00 95.85 0.00 99.46 0 0.000 0 0.000 0.000
89.95 0.00 0.00 1.93 60.00 2 0.199 2 0.003 0.202
89.95 0.00 2.08 1.91 60.15 2 0.199 2 0.003 0.202
64. CMS GC 详情:触发 Full GC
►Full GC(Serial) 的触发:
1: Concurrent-mode-failure & Promotion-mode-
failure
2: perm gen 未启用 CMSClassUnloadingEnabled
在 100% 时触发 full-gc
3: System.gc , ( 且未启用
ExplicitGCInvokesConcurrent)
可通过 -XX:+DisableExplicitGC 来禁止显式 gc
65. CMS GC 详情:触发 Full GC
Full GC(Serial) 的触发 :
Concurrent mode failure 是怎么回事?
X No more space
Young gen
old gen
对象从新生代晋升到旧生代时,需要确保旧生代有足够的内存容纳新
晋升的对象。 CMS 收集器通过统计以前的分配来预估新晋升对象的大
小,如果旧生代空间小于预估对象大小,即导致
concurrent-mode-failure ;此时会采用 CMS 的备份策略 (Serial GC)
常见的 concurrent-mode-failure 是 CMS 周期初始化太迟 ( 对象太多,
剩余空间不足 ) 所致
66. CMS GC 详情:触发 Full GC
Full GC(Serial) 的触发 :
Promotion failure 是怎么回事?
X
Young gen
old gen
对象从新生代晋升到旧生代时,由于碎片的原因,尽管总的可用空间
可能比新晋升的对象要大,但因没有连续的空间能分配给晋升对象,
导致无法晋升,触发 Full GC(Serial)
67. CMS GC 详情:触发 Full GC
► Full GC(Serial) 的触发 :
2: perm gen 不采用 cms 的话,在快 100% 时触发 full gc ( 不是 cms gc)
$ scala -J-XX:MaxPermSize=45M -J-XX:+UseConcMarkSweepGC -J-Xms1g -J-Xmx1g
scala > :cp /data/tmp/demo/
scala > :load /data/tmp/demo/import
没有开启 CMSClassUnloadingEnabled , perm gen 几乎 100% 才触发 full gc
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 84.21 94.98 4.74 98.97 25 0.500 0 0.000 0.500
89.77 0.00 56.99 5.03 99.39 30 0.540 0 0.000 0.540
85.32 0.00 6.95 5.14 85.56 34 0.556 1 0.356 0.912
0.00 71.05 10.33 5.44 86.40 39 0.590 1 0.356 0.946
68. CMS GC 详情:触发 Full GC
► Full GC(Serial) 的触发 :
3: System.gc ( 未启用 ExplicitGCInvokesConcurrent)
$ scala -J-XX:+UseConcMarkSweepGC -J-Xms1g -J-Xmx1g
scala > System.gc
注,这里仍用 jstat –gcutil 来观察, FGC=1 表示一次 Full GC ,这不太严禁,可以
用 GC log 来观察
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 93.87 0.00 99.46 0 0.000 0 0.000 0.000
0.00 0.00 95.89 0.00 99.46 0 0.000 0 0.000 0.000
0.00 100.00 9.60 1.51 99.36 1 0.169 1 0.000 0.169
0.00 0.00 2.10 1.78 60.15 1 0.169 1 0.207 0.376
0.00 0.00 2.10 1.78 60.15 1 0.169 1 0.207 0.376
70. 看看业务线用的配置
► B2B 中文站 (OracleJDK 1.6.0_25) : 25%
64 位默认是
1024k
-Xmx2g -Xms2g -Xmn512m -Xss256k
-XX:PermSize=128m -XX:MaxPermSize=196m 注意
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC Jdk6 默认
-XX:+CMSParallelRemarkEnabled 已开启
-XX:+UseCMSCompactAtFullCollection Jdk7 默认
-XX:+UseCompressedOops 关闭
-XX:+UseFastAccessorMethods
禁止 jvm 启发
-XX:+UseCMSInitiatingOccupancyOnly 式
触发 cms 。
未指定: CMSInitiatingOccupancyFraction jdk6 默认 92% 注意 concurrent-mode-failure
71. 看看业务线用的配置
► 淘宝 ( 交易 ) OpenJDK 64-Bit :
40%
-Xms4g -Xmx4g -Xmn1600m
注意场景
-XX:PermSize=256m -XX:MaxPermSize=256m
-XX:+ExplicitGCInvokesConcurrent
Jdk6 默认
-XX:+UseConcMarkSweepGC 已开启
-XX:+UseCMSCompactAtFullCollection
-XX:+CMSParallelRemarkEnabled Jdk6 已废
弃
-XX:+CMSPermGenSweepingEnabled 不需要
-XX:+CMSClassUnloadingEnabled 注意场景
-XX:+UseCMSInitiatingOccupancyOnly 待分析
-XX:CMSInitiatingOccupancyFraction=82
-XX:+HeapDumpOnOutOfMemoryError 不启用
-XX:-UseCompressedOops 指针压缩
… 其它省略
72. 看看业务线用的配置
► 天猫 (tmallsell)
62.5%
-Xms4g -Xmx4g -Xmn2560m
-XX:PermSize=96m -XX:MaxPermSize=256m
-XX:SurvivorRatio=10
-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:CMSMaxAbortablePrecleanTime=5000
-XX:CMSInitiatingOccupancyFraction=80
-XX:+CMSClassUnloadingEnabled
-XX:+UseCompressedOops
-XX:+DisableExplicitGC
-XX:+AggressiveOpts
其他省略
73. 看看业务线用的配置
► 天猫 (malllist) 50%
2 倍默认
-Xms4g -Xmx4g -Xmn2000m -Xss2m
-XX:PermSize=256m -XX:MaxPermSize=320m
-XX:SurvivorRatio=10
-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:CMSMaxAbortablePrecleanTime=5000
-XX:CMSInitiatingOccupancyFraction=80
-XX:+CMSClassUnloadingEnabled
-XX:+UseCompressedOops
-XX:+DisableExplicitGC
其他省略
74. 看看业务线用的配置
► 阿里金融 ( 贷款业务 )
系统内存的
deployMem=`echo "$MEM*6/10" | bc`m 3/5
-Xms${deployMem} -Xmx${deployMem} 最好也用变量
-Xmn1536m 来表达
-XX:PermSize=256M -XX:MaxPermSize=256M
-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:+DisableExplicitGC
其他省略
75. 看看业务线用的配置 Jdk1.4 之后
► 支付宝 ( 某 SOA 服务 ) Deprecated.
用 -Xmn 替代
-Xms768m -Xmx1280m -XX:PermSize=96m -XX:MaxPermSize=128m
-XX:NewSize=128m -XX:MaxNewSize=128m
-XX:SurvivorRatio=20000 Deprecated.
-XX:+UseParNewGC 注意场景。适用于
CPU 资源紧张或应用
-XX:+UseConcMarkSweepGC
cpu 负载较高的情况,
-XX:+UseCMSCompactAtFullCollection 一般单 cpu 才考虑,
-XX:+CMSIncrementalMode 默认不开启 . 可能该服
-XX:+CMSPermGenSweepingEnabled 务器上部署了多个实例
-XX:CMSInitiatingOccupancyFraction=1
-XX:CMSFullGCsBeforeCompaction=0 会频繁 gc?
-XX:CMSIncrementalDutyCycleMin=10 默认就是 0
-XX:CMSIncrementalDutyCycle=30
-XX:CMSMarkStackSize=8M 增量模式参数
-XX:CMSMarkStackSizeMax=32M
-XX:MaxTenuringThreshold=0
其他省略 0 表示不经过
Survivor 直接进
入老生代
76. 看看业务线用的配置
► 良无限 12.5%
-Xms2g -Xmx2g -Xmn256m
-XX:PermSize=128m -Xss256k
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70
77. 看看业务线用的配置
► 阿里云 ( 邮箱 ) 12.5%
-Xmx2g -Xms2g -Xmn256m -Xss256k
-XX:PermSize=128m
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70
78. 引用 & 推荐
博客 :
http://blog.ragozin.info/2011/06/understanding-gc-pauses-in-jvm-hotspots.html
http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html
https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs
https://blogs.oracle.com/jonthecollector/entry/the_real_thing
http://stackoverflow.com/questions/7065337/intern-behaving-differently-in-jdk6-and-jdk7
http://javaeesupportpatterns.blogspot.com/2011/10/java-7-features-permgen-removal.html
http://blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead/
http://hllvm.group.iteye.com/group/topic/27945
http://embedded.soe.ucsc.edu/pubs/sigplan98-1/short.html
http://rednaxelafx.iteye.com/blog/
书籍 :
《深入 java 虚拟机》
《分布式 Java 应用:基础与实践》
《垃圾收集》
Editor's Notes 可能 jdk7 也改进了相似字符串的压缩?