Java虚拟机(四)GC调优

1、JVM的内存结构
(1)运行时数据区(规范)
                                            

——程序计数器(Program Counter register):还一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。它是线程私有的。
        JVM 支持多线程同时执行,每一个线程都有自己的PC Register,线程正在执行的方法叫做当前方法,如果是java代码,PC Register里面存放的就是当前正在执行的指令的地址,如果是C代码,则为空
——虚拟机栈(Virtual Machine Stacks):也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链表、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
——本地方法栈(Native Method Stack):与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务
——(Heap):是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动是创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续的即可,就像我们的磁盘空间一样。
——方法区(Method Area):与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-heap(非堆),目的是与Java堆区分开来。
        在JDK8里是MetaSpace,在JDK8之前是PermSpace
——运行时常量池(Runtime Constant Pool):是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
(2)JVM的内存结构
                                       

        整个JVM的内存结构可以分为堆区和非堆区两大块,其中堆区又分为两大块:Young区和Old区;Young区又分为Survivor区和Eden区;Survivor区是分为两个同样大小的部分,一个是S0(From Survivor),另一个是S1(To Survivor),在同一个时间点上S0和S1只有一个是有值的,另一个是空的。非堆区在JDK8里有一个新的名字MetaSpace,这部分又包含压缩类空间(CCS)和CodeCache(JIT编译的Native代码)

MetaSpace    Class、Package、Method、Field、字节码、常量池、符号引用
CCS32位指针的Class
CodeCache JIT编译后的本地代码,JNI使用的C代码

 (3)常用参数设置
①-Xms、-Xmx:最小、最大堆内存
②-XX:NewSize、-XX:MaxNewSize:新生代大小、最大新生代大小
③-XX:NewRatio、-XX:SurvivorRatio:新生代与老年代的比例、Eden区与Survivor区的比例
-XX:MetaspaceSize、-XX:MaxMetaspaceSize:Matespace区大小、最大Matespace区大小(主要是调这个参数)
⑤-XX:+UseCompressedClassPointers:启用压缩类指针
⑥-XX:CompressedClassSpaceSize:压缩类空间的大小,没有设置,默认是1G
⑦-XX:InitialCodeCacheSize:初始化CodeCache的大小
⑧-XX:ReservedCodeCacheSize:CodeCache的最大大小
2、垃圾回收算法
思想:枚举根节点、做可达性分析
                                          
根节点:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等
(1)标记-清除——算法分为”标记“和”清除“两个阶段:首先标记所有需要回收的对象,在标记完成后统一回收所有
                            缺点:效率不高,标记和清除两个过程的效率都不高;产生碎片,碎片太多会导致提前GC
                                    
(2)复制——他将可用内存按容量划分为大小相等的两块,每次只是用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清除掉(Young区就是使用的这种算法)
                      优缺点:实现简单,运行高效,但是空间利用率低
                                      
(3)标记-整理——标记过程仍然与”标记-清除“算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端到边界以外的内存
                      优缺点:没有了内存碎片,但是整理内存比较耗时
                                   
(4)分代收集:在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法;而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用”标记-清除“或”标记-整理“算法进行收回。
3、对象分配
(1)对象优先在Eden分配
(2)大对象直接进入老年代:
           可以通过设置 -XX:PretenureSizeThresgold 指定多大的对象是大对象
(3)长期存活的对象进入老年代:
           长期存活参数设置 -XX:MaxTenuringThreshold;
                 -XX:PrintTenuringDistribution——发生Young GC时打印一下存活对象年龄的信息
                 -XX:TargetSurvivorRatio——经过一次Young GC存活的比例
4、垃圾收集器
(1)串行收集器Serial:Serial、Serial Old
        参数设置:-XX:+UseSerialGC      -XX:+UseSerialOldGC     (新生代和老年代中使用)
(2)并行收集器Parallel:Parallel Scavenge、Parallel Old(吞吐量优先)
        参数设置:-XX:+UseParallelGC      -XX:+UseParallelOldGC
        Server模式下的默认收集器
(3)并发收集器Concurrent:CMS、G1(停顿时间优先)
        参数设置:-XX:+UseConcMarkSweepGC      -XX:UseParNewGC        -XX:+UseG1GC
        ——并行(Parallel):之多余垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。适合科学计算、后台处理等弱交换场景。
        ——并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),垃圾收集线程在执行的时候不会停顿用户程序的运行。适合对响应时间有要求的场景,比如:web。
        ——停顿时间:垃圾收集器做垃圾回收中断应用执行的时间。参数设置:-XX:MaxGCPauseMillis
        ——吞吐量:花在垃圾收集的时间和花在应用时间的占比。参数设置:-XX:GCTimeRatio=<n>,垃圾收集时间占比:1/(1+n)
垃圾收集器搭配
                                                   
Q:如何选择垃圾收集器?
A:①优先调整堆的大小让服务器自己来选择
      ②如果内存小于100M,使用串行收集器
      ③如果是单核,并且没有停顿时间的要求,串行或者JVM自己选择
      ④如果允许停顿时间超过1s,选择并行或者JVM自己选择
      ⑤如果响应时间最重要,并且不能超过1秒,使用并发收集器
· Parallel Collector
         -XX:+UseParallelGC手动开启,Server默认开启
         -XX:ParallelGCThreads=<N>多少个GC线程  
             CPU>8  N = 5/8
             CPU<8  N = CPU
Parallel Collector Ergonomics(自适应)
        -XX:MaxGCPauseMillis=<N>
        -XX:GCTimeRatio=<N>
        -Xmx <N>
动态内存调整
        -XX:YoungGenerationSizeIncrement=<Y> (默认20%)
        -XX:TenuredGenerationSizeIncrement=<T>(默认20%)
        -XX:AdaptiveSizeDecrementScaleFactor=<D>(默认4%)
· CMS Collector
         并发收集、低停顿、低延迟、老年代收集
         收集过程:①CMS initial mark:初始标记Root,STW(stop the world);
                          ②CMS concurrent mark:并发标记;
                          ③CMS-concurrent-preclean:并发预清理
                          ④CMS remark:重新标记,STW;
                          ⑤CMS concurrent sweep:并发清除。
          缺点:CPU敏感、会产生浮动垃圾、会产生空间碎片
          相关参数:-XX:ConcGCThreads  并发的GC线程数
                           -XX:+UseCMSCompactAtFullCollection         Full GC之后做压缩一次
                           -XX:CMSInitiatingOccupancyFraction             触发FullGC(90%+)
                           -XX:+UseCMSInitiatingOccupancyOnly         是否动态调整
                           -XX:+CMSScavengeBeforeRemark                 FullGC之前先做YGC
                           -XX:+CMSClassUnloadingEnable                    启用回收Perm区(JDK8之前)
          iCMS:增量的CMS,适用于单核或者双核
· G1 Collector
         JDK7正式使用,新生代和老生代收集器,大内存、停顿时间也短
         Region
         SATB:Snapshot-At-The-Beginning,它是通过Root Tracing得到的,GC开始时候存活对象的快照。
         RSet:记录了其他Region中的对象引用本Region中对象的关系属于points-into结构(谁引用了我的对象)
Young GC
         新对象进入Eden区、存活对象拷贝到Survivor区、存活时间达到年龄阈值时,对象晋升到Old区
Mixed GC
         不是FullGC,回收所有的Young和部分Old
         global concurrent marking(全局并发标记)
                    ①Initial marking phase:标记GC Root,STW;
                    ②Root region scanning phase:标记存活Region;
                    ③Concurrent marking phase:标记存活的对象;
                    ④Remark phase:重新标记,STW;
                    ⑤Cleanup phase:部分STW。
          相关参数:
                  发生时机:InitiatingHeapOccupancyPercent:堆占有率达到这个数值则触发global concurrent marking默认45%;
                  G1HeapWastePercent:在global concurrent marking结束之后,可以知道区有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC;
                  G1MixedGCLiveThresholdPercent:Old区的region被回收时的存活对象占比;
                  G1MixedGCCountTarget:一次global concurrent marking之后,最多执行Mixed GC的次数;
                  G1OldCSetRegionThresholdPercent:一次Mixed GC中能被选入CSet的最多old区的region数量;
           常用参数:
                  -XX:UseG1GC开启G1
                  -XX:G1HeapRegionSize=n,region的大小,每个大小1-32M,最多2048个
                  -XX:MaxGCPauseMillis=200  最大停顿时间
                  -XX:G1NewSizePercent、-XXG1MaxNewSizePercent
                  -XX:G1ReservePercent=10  保留防止to space溢出
                  -XX:ParallelGCThreads=n   SWT线程数
                  -XX:ConcGCThreads=n  并发线程数=(1/4)并行
5、可视化GC日志分析工具
(1)在线工具:http://gceasy.io/
(2)GCViewer:
(3)打印日志的相关参数
         -XX:+PrintGCDetails、-XX:+PrintGCTimeStamps、-XX:+PrintGCDateStamps、
         -Xloggc:$CATALINA_HOME/logs/gc.log、-XX:+PrintHeapAtGC、-XX:+PrintTenuringDistribution
(4)日志格式:Parallel GC、CMS GC、G1 Young GC、G1 Mixed GC(略。。。)
6、GC调优步骤
(1)打印GC日志
(2)根据日志得到关键的性能指标(吞吐量+响应时间)
(3)分析GC原因,调优JVM参数
(4)实战GC调优:
         初始设置:-XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_HOME/logs/ -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log
         Parallel GC调优的指导原则:
               ①除非确定,否则不要设置最大堆内存;
               ②优先设置吞吐量目标;
               ③如果吞吐量目标达不到,调大最大内存,不能让OS使用Swap,如果仍然达不到,降低目标;
               ④吞吐量能达到,GC时间太长,设置停顿时间的目标
         G1 GC调优最佳实践:
               ①年轻代大小:避免使用-Xmn、-XX:NewRatio等显式设置Young区大小,会覆盖暂停时间目标;
               ②停顿时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序和10%的垃圾回收时间,太严苛会直接影响到吞吐量;
               ③关于MixGC调优设置:-XX:InitiatingHeapOccupancyPercent、-XX:G1MixedGCLiveThresholdPercent、-XX:G1HeapWastePercent
                   -XX:G1MixedGCCountTarget、-XX:G1OldCSetRegionThresholdPercent
            是否需要切换到G1:①50%以上的堆被存活的对象占用;②对象分配和晋升的速度变化非常大;③垃圾回收时间特别长,超过1秒
7、参考资料
jvm的运行时数据区
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

Metaspace
http://ifeve.com/jvm-troubleshooting-guide-4/

压缩类空间
https://blog.csdn.net/jijijijwwi111/article/details/51564271

CodeCache
https://blog.csdn.net/yandaonan/article/details/50844806
http://engineering.indeedblog.com/blog/2016/09/job-search-web-app-java-8-migration/

GC调优指南:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html

如何选择垃圾收集器
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html

G1最佳实践
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations

G1 GC的一些关键技术
https://zhuanlan.zhihu.com/p/22591838

CMS日志格式
https://blogs.oracle.com/poonam/understanding-cms-gc-logs

GC之详解CMS收集过程和日志分析
http://www.cnblogs.com/zhangxiaoguang/p/5792468.html

G1日志格式
https://blogs.oracle.com/poonam/understanding-g1-gc-logs

理解G1垃圾收集器日志理解G1垃圾收集器日志
https://blog.csdn.net/zhanggang807/article/details/46011341

GC日志分析工具
http://gceasy.io/

GCViewer
https://github.com/chewiebug/GCViewer

ZGC
http://openjdk.java.net/jeps/333

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讲文明的喜羊羊拒绝pua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值