SlideShare a Scribd company logo
1 of 78
JVM 内存管理基础




    hongjiang 2012.7
说明
►    课程目的
    帮助 Java 程序员更好的认识 JVM ,掌握 GC 基础知识,了解现有线上业务的 GC 配置及问
    题

►     大纲
    1: JVM 内存结构
          从对象的创建和消亡过程看内存
    2: GC 的选择
          了解各种 GC 的特点,选择的依据
    3: CMS GC 详情
          详细了解 CMS GC 的特点
    4: 当前业务线线上的 GC 配置
          参考集团各子公司业务线的 GC 配置以及问题

►     学习对象要求
    Java 程序员 <= P6

►    联系我: http://laiwang.com/u/3001
第一部分


从对象的创建与消亡过程看 JVM 内存
JVM 运行时数据区
    heap               non-heap




                    Native
             VM                   Hotspot 里
                    Method
            Stack                 本地方法栈
                     Stack
堆空间,不同的                           与 java 栈在
GC 策略有不同的   Program counter          一起
   划分方式         register


              Method Area
类的初始化
►   引子,初始化问题

public class TestA{
  static { a = 1; }
  static int a = 0; // 1) 如果不赋值呢? 2) 如果这一句与上一句位置互换?
  public static void main(String[] args){
    System.out.println(TestA.a);
  }
}
例子 2
►   模板模式使用不当遇到的问题
类的初始化:生命周期



Load               Link       Initialize
加载                 连接          初始化




                          ?          <clinit>
       classload
类的初始化:生命周期
   展开其中的 Link 过程,又有三个步骤:
   看另一段初始化的代码



        Verify                        Prepare                        Resolve
       (校验)                           (准备)                           (解析)




                                              为类变量分配内存,设置
                                              默认值。注意:此时在
                                                 <clinit> 之前


http://frankkieviet.blogspot.com/2009/03/javalanglinkageerror-loader-constraint.html
//FIXME
类的初始化: <clinit> 的问题
►    此过程的数据主要在哪个区域?

►    看一段代码例子:
    中文站曾大面积发生过的现象 ( 高并发情况
    下)
     基础框架里 (toolkit.common.lang) 里潜伏
    的

简化后的代码:
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!");
  }
}
诊断方法
►   jstack javapid > file

►   printf “%0x” threadId

对工具的使用另外展开一个话题,参考:
http://www.slideshare.net/hongjiang/effective-linux2tools
http://www.slideshare.net/hongjiang/effective-linux3diagnosis
类的初始化:
   运行时数据区与线程安全
         heap             non-heap




                       VM
                                     线程私有
                      Stack
  除了在 Eden 区
 每个线程分配一块
缓冲 (TLAB) ,在该区   Program counter
                     register        线程私有
  是线程私有。
其他都是线程共享的
                    Perm gen         线程共享
类的初始化:
        运行时数据区与线程安全

►   <clinit> 更新类的数据是在 perm gen 区域

► 当多个线程需要初始化一个类,仅仅运行一
    个线程来进行,其他线程需要等待
    (object.wait) 。当活动的线程完成初始化之
    后,它必须通知其他等待线程。
类的初始化: PermGen 的数据内容

Class mate data
                   基本类型信息          动态连接
类型信息                               依赖的数
                    类型常量池            据

                     字段信息
                                   字节码
                     方法信息

                  类成员 ( 静态变量 )
                                    通过
                  classloader 引用
                                   <clinit>
                                   来修改
                  Class 类实例的引用
类的初始化
►     常量池的内容 // TODO

►     怎么查看常量池中的字符串常量
    // 参考 PermStat.java 的实现
     class StringPrinter implements StringTable.StringVisitor
类的初始化: <clinit> ,与内存无关的题外话

下面的代码是否会有 <clinit> 方法?

public class A{
  public static final String FLAG = “hi”;
}

引申,另一个中文站发生过的案例
$ javap –verbose
类的初始化: PermGen 区域的变数
► 未来 Jdk7 的某个 update 可能将 PermGen
 彻底去除, class metadata 将放入 C-heap
 区域。 Hotspot 与 JRockit 整合。

► java7目前 release 的版本中,已经将字符串
 常量池移到了 main heap ( 对于分代 gc 来说
 是 old gen 区域 )

看一个例子:
类的初始化: PermGen 区域的变数
1 ) 调用 String.intern()
scala> for(i <- 0 to 3000000)
new String("xxxxxxxxxxxxxxxxx" + i).intern

对比 jdk6 与 jdk7 的差异

2 )产生随机的大字符串做常量,对比 jdk6/7
看看存在哪儿?
对象的创建
► 当你创建一个对象实例时,被放到了哪儿?


 Stack ?
   有可能么?
 Heap ?
   Eden ? Old Gen ?
对象的创建
►    了解一个前提:基于逃逸分析 (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 默认已开启
对象的创建: Allocations
1) 在 stack 上创建(未来可能)最高效
   ,避免了 GC
                          ①   Stack



                          ②    TLAB
2) 在 TLAB 区域创建,避免了竞争的代
                               Heap
   价 ( 默认 )                   ( Eden)
                          ③
3) 在 Eden 区域创建 ( 默认 )
                          ④    Heap
                               (Old)
4) 在 Old 区创建,需满足条件,比如对
   象大于阈值
对象的创建: Allocations
►    fast allocation
    1) bump the pointer
    前提:有大块的连续空闲空间
    内部维护一个指针( allocatedTail ),它始终指向先前已分配对象的尾部,当新的对象分
    配请求到来时,只需检查代中剩余空间(从 allocatedTail 到代尾 geneTail )是否足以容纳
    该对象



    2) thread local allocation buffers(TLABs)
    避免了多线程竞争同步的开销
    在 TLABs 区域也可以采用 bump-the-pointer 方式
对象的创建: 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/
对象的创建: TLAB
►   TLAB 的参数

-XX:+UseTLAB     启用 TLAB 默认开启
-XX:+ResizeTLAB 动态设置每个线程的 tlab 大小 默认开启
-XX:TLABSize=X TLAB 初始大小 默认 0
-XX:TLABWasteTargetPercent=X 占 Eden 区的百分比 默认 1%

-XX:PrintTLAB   打印 TLAB 相关信息 默认不开启
对象的创建 : Eden
►    在 Eden 区分配
    TLABs 未开启或无可用空间

►    Eden 区空间不足时
    minor gc  存活对象从 Eden 移动到
    Survivor
    Survivor 区空间不足则晋升到老生代
对象的创建 : 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 的新生代采用自适应策略自动调节各区域空间
指定区域大小的参数与 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
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 内
     存。
对象的消亡
►       前提,了解引用类型
       强引用 ( 默认 )
       软引用 (soft reference)
       弱引用 (weak reference)
       幽灵引用 (phantom reference)

►       为什么引入另外三种 Reference ?
        程序与 GC 有了交互方式
对象的消亡: reference
          reachable
强可达 strong reachable

                        线程根引用


                强引用

                                      C
                   A
                       强引用

                                     软引用
                             B


                       B 不会被 GC 回收
对象的消亡: reference
           reachable
软可达 soft reachable


                            线程根引用


        软引用      软引用

                                          C
                      A
                           软引用
            D
                                        软引用
                     强引用         B


                          内存不足时, B 会被 GC 回收
     SoftRef 的存活时间 /M 空闲空间: -XX:SoftRefLRUPolicyMSPerMB 默认
     1秒
对象的消亡: reference
           reachable
弱可达 weak reachable


                           线程根引用


        弱引用     弱引用

                                      C
                      A
                           弱引用
           D
                                     弱引用
                     强引用         B



                 GC 触发时 B 会被回收
对象的消亡: ReferenceQueue

                                           监听器
                                           eg1 : 对 FinalReference 类型
                                              ref 计数,执行其 finalize
                                              方法
                      pending 链表
                                             ReferenceQueue

GC                next         pending       ReferenceQueue

                                             ReferenceQueue
     discovered ref
                                   ReferenceHandler
                                   线程负责轮询 pending 链表,
                                   根据 queue 的类型决定是否将
                                   元素放入 queue
ReferenceQueue 的案例
►     从这篇帖子说起
    “WeakHashMap 的神话” : http://www.javaeye.com/topic/587995
结合 WeakHashMap 的代码,分析一下作者的问题
对象的消亡: 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 导致对象复活
 幽灵引用的用途
类的消亡:卸载 Class
1) 系统 classloader 加载的 Class 不会被卸载
只有非系统 classloader 加载的 Class 才有可能
  被卸载。

2) Class 若有 static field 或 static block ,卸载
  时除了没有可达引用,还需要其 classloader
  也没有被使用才行。 Why ?
  假设可以被卸载,当需要重新加载时, static field 被重新初始化,其
 状态可能与之前不一致; static 构造块也会重新执行,与预期不一致,
 或许引起副作用。
类的消亡: 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
第二部分

GC 的选择,以及 CMS GC 详解
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 的备用 ) 组合
GC 的选择:新生代与旧生代的组合
                                                           吞吐量优先
                                                          可自适应调节
                                                          新生代各区域
                                                          Jdk1.4 提供



                                             Parallel
                 Serial       ParNew
                                            Scavenge




                 CMS
Jdk1.5                                     Parallel Old
 提供                           Serial/
                          PS MarkSweep




     作为 CMS 的备
     份,在 CMS 并                    PS 的旧生代版本
     发失败时采用                         Jdk1.6 提供
GC 的选择: -XX:+UseSerialGC
采用 Serial + Serial Old 组合 , client 模式默认是此方式



                                Parallel
       Serial     ParNew
                               Scavenge




                    Serial/
        CMS                    Parallel Old
                PS MarkSweep
GC 的选择: -XX:
              +UseParallelGC
采用  Parallel Scavenge + Serial Old(PS MarkSweep) 组合,
 server 模式默认


                                      Parallel
          Serial       ParNew
                                     Scavenge




                         Serial/
          CMS                       Parallel Old
                     PS MarkSweep
GC 的选择: -XX:
       +UseParNewGC
采用 ParNew + Serial Old 组合

                               Parallel
      Serial     ParNew
                              Scavenge




                   Serial/
      CMS                     Parallel Old
               PS MarkSweep
GC 的选择: -XX:+UseParallelOldGC
  采用 Parallel Scavenge + Parallel Old 组
 合
                                Parallel
       Serial     ParNew
                               Scavenge




                    Serial/
        CMS                    Parallel Old
                PS MarkSweep
GC 的选择: -XX:
       +UseConcMarkSweepGC
采用 ParNew + CMS + Serial Old(CMS 的备用 ) 组
 合

                                      Parallel
        Serial         ParNew
                                     Scavenge




        CMS               Serial/    Parallel Old
                 备用   PS MarkSweep
GC 的选择: -XX:
        +UseConcMarkSweepGC
          -XX:- UseParNewGC
采用 Serial+ CMS + Serial Old(CMS 的备用 ) 组合

                                       Parallel
         Serial         ParNew
                                      Scavenge




        CMS                Serial/    Parallel Old
                  备用   PS MarkSweep
GC 的选择:策略依据
►     从应用场景:
    1) web app ,用户响应优先;
    2) 任务,吞吐率优先

►    从成熟度和稳定性
    生产环境选择一种策略前需要大量测试
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 主要采用拷贝算法
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
Major GC
►   只分享 CMS GC

►   CMS GC 与 Full gc 是一回事么?
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 的情况另说 )
CMS GC 的详细过程
①   Initial marking                   Root Set



仅标记根集合
直接可达对象 ,                    A1        B1         C1   D1
记录在外部的
   bitmap
to-be-scanned
                                 A3   B2
                       A2




                      A4
CMS GC 的详细过程
②   Concurrent marking                  Root Set




                              A1        B1              C1          D1

 marking 的过程即
 遍历所有对象,进
   行着色,                            A3   B2         借助 markingStack 栈完成遍历,
 最费时的一个过程                A2                        可通过下面参数设置其大小
                                                   -XX:CMSMarkStackSize=xxx
                                                   ( 默认 32k)
                                                   -XX:CMSMarkStackSizeMax=yyy
                         A4                        ( 默认 4M)
                                                   采用栈的方式,就有栈溢出的可能
                                                   ,不过实际过程有很多技巧,未探
                                                   究
CMS GC 的详细过程
②   Concurrent marking

       此过程 minor gc 和用户线程也是可以工作的;
       怎么应对 minor gc 和用户线程对数据修改不一致 ?



                                                   Heap



                                                  Card Table

                                                 Mod Union Table


                划分为多个区域 (card) ,如果该区
                域用户线程改变数据发生不一致                记录 minor gc
                 (dirty) ,则记录在 card table 中    修改的 card
CMS GC 的详细过程
②   preclean



       新增的一个优化步骤,减少下一阶段 (remark) stop-the-world 的时间


     -XX:CMSMaxAbortablePrecleanTime=5000
CMS GC 的详细过程
③   Final marking (stop-the-world)




                                     C   D       F
                 A                                   G
                              B              E




              该区域对象发生
               过变化 (dirty)
               重新 marking
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 后整理                       内存 ( 适合于大片连续的空间 )
CMS GC 详情 : 浮动垃圾
► 浮动垃圾的产生




  图中的对象 c 为浮动垃圾,需要等下次 gc 才能回收。
  http://research.sun.com/techrep/2000/smli_tr-2000-88.pdf
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
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
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
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
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
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
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 周期初始化太迟 ( 对象太多,
    剩余空间不足 ) 所致
CMS GC 详情:触发 Full GC
   Full GC(Serial) 的触发 :
   Promotion failure 是怎么回事?

                              X

Young gen
                                  old gen


    对象从新生代晋升到旧生代时,由于碎片的原因,尽管总的可用空间
    可能比新晋升的对象要大,但因没有连续的空间能分配给晋升对象,
    导致无法晋升,触发 Full GC(Serial)
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
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
第三部分

业务线配置参考
看看业务线用的配置
  ►   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
看看业务线用的配置
►    淘宝 ( 交易 ) 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                   指针压缩
    … 其它省略
看看业务线用的配置
►   天猫 (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
      其他省略
看看业务线用的配置
►   天猫 (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
      其他省略
看看业务线用的配置
► 阿里金融 ( 贷款业务 )

                                          系统内存的
 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
   其他省略
看看业务线用的配置                                  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 直接进
                                                          入老生代
看看业务线用的配置
►   良无限                        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
看看业务线用的配置
►   阿里云 ( 邮箱 )                 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
引用 & 推荐
博客 :
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 应用:基础与实践》
《垃圾收集》

More Related Content

What's hot

JVM内容管理和垃圾回收
JVM内容管理和垃圾回收JVM内容管理和垃圾回收
JVM内容管理和垃圾回收Tony Deng
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)wang hongjiang
 
Java Crash分析(2012-05-10)
Java Crash分析(2012-05-10)Java Crash分析(2012-05-10)
Java Crash分析(2012-05-10)Kris Mok
 
Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析wang hongjiang
 
Java cpu
Java cpuJava cpu
Java cpuykdsg
 
Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)ykdsg
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)wang hongjiang
 
并发编程交流
并发编程交流并发编程交流
并发编程交流bluedavy lin
 
Btrace intro(撒迦)
Btrace intro(撒迦)Btrace intro(撒迦)
Btrace intro(撒迦)ykdsg
 
为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)Kris Mok
 
线程与并发
线程与并发线程与并发
线程与并发Tony Deng
 
UseNUMA做了什么?(2012-03-14)
UseNUMA做了什么?(2012-03-14)UseNUMA做了什么?(2012-03-14)
UseNUMA做了什么?(2012-03-14)Kris Mok
 
高性能的Java代码编写及常见问题排查
高性能的Java代码编写及常见问题排查高性能的Java代码编写及常见问题排查
高性能的Java代码编写及常见问题排查bluedavy lin
 
IOS入门分享
IOS入门分享IOS入门分享
IOS入门分享zenyuhao
 
Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇bluedavy lin
 
Mysql展示功能与源码对应
Mysql展示功能与源码对应Mysql展示功能与源码对应
Mysql展示功能与源码对应zhaolinjnu
 
HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享Chong-Kuan Chen
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析Justin Lin
 
OpenEJB - 另一個選擇
OpenEJB - 另一個選擇OpenEJB - 另一個選擇
OpenEJB - 另一個選擇Justin Lin
 

What's hot (20)

JVM内容管理和垃圾回收
JVM内容管理和垃圾回收JVM内容管理和垃圾回收
JVM内容管理和垃圾回收
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)
 
Java Crash分析(2012-05-10)
Java Crash分析(2012-05-10)Java Crash分析(2012-05-10)
Java Crash分析(2012-05-10)
 
Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析
 
Java cpu
Java cpuJava cpu
Java cpu
 
Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)
 
并发编程交流
并发编程交流并发编程交流
并发编程交流
 
Btrace intro(撒迦)
Btrace intro(撒迦)Btrace intro(撒迦)
Btrace intro(撒迦)
 
为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)
 
线程与并发
线程与并发线程与并发
线程与并发
 
UseNUMA做了什么?(2012-03-14)
UseNUMA做了什么?(2012-03-14)UseNUMA做了什么?(2012-03-14)
UseNUMA做了什么?(2012-03-14)
 
高性能的Java代码编写及常见问题排查
高性能的Java代码编写及常见问题排查高性能的Java代码编写及常见问题排查
高性能的Java代码编写及常见问题排查
 
IOS入门分享
IOS入门分享IOS入门分享
IOS入门分享
 
Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇
 
Mysql展示功能与源码对应
Mysql展示功能与源码对应Mysql展示功能与源码对应
Mysql展示功能与源码对应
 
HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
OpenEJB - 另一個選擇
OpenEJB - 另一個選擇OpenEJB - 另一個選擇
OpenEJB - 另一個選擇
 
JVM及其调优
JVM及其调优JVM及其调优
JVM及其调优
 

Similar to Jvm内存管理基础

Java垃圾收集原理
Java垃圾收集原理Java垃圾收集原理
Java垃圾收集原理yin gong
 
Java垃圾收集原理
Java垃圾收集原理Java垃圾收集原理
Java垃圾收集原理yin gong
 
Java GC Tuning
Java GC TuningJava GC Tuning
Java GC Tuningpprun
 
Cassandra简介.ppt
Cassandra简介.pptCassandra简介.ppt
Cassandra简介.pptjames tong
 
Jvm那些事
Jvm那些事Jvm那些事
Jvm那些事dynamiclu
 
Sun jdk-1.6-gc
Sun jdk-1.6-gcSun jdk-1.6-gc
Sun jdk-1.6-gc锐 张
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509tidesq
 
Sun jdk 1.6内存管理 -使用篇-毕玄
Sun jdk 1.6内存管理 -使用篇-毕玄Sun jdk 1.6内存管理 -使用篇-毕玄
Sun jdk 1.6内存管理 -使用篇-毕玄锐 张
 
Linux内存管理
Linux内存管理Linux内存管理
Linux内存管理zijia
 
Linux内存管理
Linux内存管理Linux内存管理
Linux内存管理zijia
 
Web请求异步处理和海量数据即时分析在淘宝开放平台的实践
Web请求异步处理和海量数据即时分析在淘宝开放平台的实践Web请求异步处理和海量数据即时分析在淘宝开放平台的实践
Web请求异步处理和海量数据即时分析在淘宝开放平台的实践mysqlops
 
Oracle rac资源管理算法与cache fusion实现浅析
Oracle rac资源管理算法与cache fusion实现浅析Oracle rac资源管理算法与cache fusion实现浅析
Oracle rac资源管理算法与cache fusion实现浅析frogd
 
Java内存管理问题案例分享
Java内存管理问题案例分享Java内存管理问题案例分享
Java内存管理问题案例分享bluedavy lin
 
C++工程实践
C++工程实践C++工程实践
C++工程实践Shuo Chen
 
Simple tech-talk
Simple tech-talkSimple tech-talk
Simple tech-talkliltos
 
分布式系统缓存设计
分布式系统缓存设计分布式系统缓存设计
分布式系统缓存设计zhujiadun
 
Lamp高性能设计
Lamp高性能设计Lamp高性能设计
Lamp高性能设计锐 张
 
美团技术沙龙04 - Kv Tair best practise
美团技术沙龙04 - Kv Tair best practise 美团技术沙龙04 - Kv Tair best practise
美团技术沙龙04 - Kv Tair best practise 美团点评技术团队
 
分布式系统缓存设计
分布式系统缓存设计分布式系统缓存设计
分布式系统缓存设计aleafs
 

Similar to Jvm内存管理基础 (20)

Java垃圾收集原理
Java垃圾收集原理Java垃圾收集原理
Java垃圾收集原理
 
Java垃圾收集原理
Java垃圾收集原理Java垃圾收集原理
Java垃圾收集原理
 
Java GC Tuning
Java GC TuningJava GC Tuning
Java GC Tuning
 
Cassandra简介.ppt
Cassandra简介.pptCassandra简介.ppt
Cassandra简介.ppt
 
Jvm那些事
Jvm那些事Jvm那些事
Jvm那些事
 
Sun jdk-1.6-gc
Sun jdk-1.6-gcSun jdk-1.6-gc
Sun jdk-1.6-gc
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
 
Sun jdk 1.6 gc
Sun jdk 1.6 gcSun jdk 1.6 gc
Sun jdk 1.6 gc
 
Sun jdk 1.6内存管理 -使用篇-毕玄
Sun jdk 1.6内存管理 -使用篇-毕玄Sun jdk 1.6内存管理 -使用篇-毕玄
Sun jdk 1.6内存管理 -使用篇-毕玄
 
Linux内存管理
Linux内存管理Linux内存管理
Linux内存管理
 
Linux内存管理
Linux内存管理Linux内存管理
Linux内存管理
 
Web请求异步处理和海量数据即时分析在淘宝开放平台的实践
Web请求异步处理和海量数据即时分析在淘宝开放平台的实践Web请求异步处理和海量数据即时分析在淘宝开放平台的实践
Web请求异步处理和海量数据即时分析在淘宝开放平台的实践
 
Oracle rac资源管理算法与cache fusion实现浅析
Oracle rac资源管理算法与cache fusion实现浅析Oracle rac资源管理算法与cache fusion实现浅析
Oracle rac资源管理算法与cache fusion实现浅析
 
Java内存管理问题案例分享
Java内存管理问题案例分享Java内存管理问题案例分享
Java内存管理问题案例分享
 
C++工程实践
C++工程实践C++工程实践
C++工程实践
 
Simple tech-talk
Simple tech-talkSimple tech-talk
Simple tech-talk
 
分布式系统缓存设计
分布式系统缓存设计分布式系统缓存设计
分布式系统缓存设计
 
Lamp高性能设计
Lamp高性能设计Lamp高性能设计
Lamp高性能设计
 
美团技术沙龙04 - Kv Tair best practise
美团技术沙龙04 - Kv Tair best practise 美团技术沙龙04 - Kv Tair best practise
美团技术沙龙04 - Kv Tair best practise
 
分布式系统缓存设计
分布式系统缓存设计分布式系统缓存设计
分布式系统缓存设计
 

More from wang hongjiang

中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型wang hongjiang
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closureswang hongjiang
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)wang hongjiang
 

More from wang hongjiang (9)

中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型
 
Scala类型系统
Scala类型系统Scala类型系统
Scala类型系统
 
functional-scala
functional-scalafunctional-scala
functional-scala
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
泛型总结
泛型总结泛型总结
泛型总结
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)
 
聊一些电影
聊一些电影聊一些电影
聊一些电影
 
善用工具
善用工具善用工具
善用工具
 
Enum开锁
Enum开锁Enum开锁
Enum开锁
 

Jvm内存管理基础

  • 1. JVM 内存管理基础 hongjiang 2012.7
  • 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); } }
  • 6. 例子 2 ► 模板模式使用不当遇到的问题
  • 7. 类的初始化:生命周期 Load Link Initialize 加载 连接 初始化 ? <clinit> classload
  • 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
  • 16. 类的初始化: <clinit> ,与内存无关的题外话 下面的代码是否会有 <clinit> 方法? public class A{ public static final String FLAG = “hi”; } 引申,另一个中文站发生过的案例 $ javap –verbose
  • 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
  • 44. GC 的选择: -XX:+UseParallelOldGC 采用 Parallel Scavenge + Parallel 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

  1. 可能 jdk7 也改进了相似字符串的压缩?