• jvm参数调优


    一、了解JVM参数

    JVM参数类型:

      • 标准参,所有JVM都应该支持
    • -X 非标,每个JVM实现不同
    • -XX 不稳定参数,下个版本可能取消

    常见垃圾回收器组合参数设定:(1.8)

    • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old

      • 小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
    • -XX:+UseParNewGC = ParNew + SerialOld

      • 这个组合已经很少用(在某些版本中已经废弃)
    • -XX:+UseConcMarkSweepGC = ParNew + CMS + Serial Old

    • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】

    • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old

    • -XX:+UseG1GC = G1

    • Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC

      • java +XX:+PrintCommandLineFlags -version
      • 通过GC的日志来分辨
    • Linux下1.8版本默认的垃圾回收器到底是什么?

      • 1.8.0_181 默认(看不出来)Copy MarkCompact
      • 1.8.0_222 默认 PS + PO

    GC常用参数

    • -Xmn -Xms -Xmx -Xss
      年轻代 最小堆 最大堆 栈空间

    • -XX:+UseTLAB
      使用TLAB,默认打开,(一般不需要调整)

    • -XX:+PrintTLAB
      打印TLAB的使用情况,(一般不需要调整)

    • -XX:TLABSize
      设置TLAB大小,(一般不需要调整)

    • -XX:+DisableExplictGC,要默认打开,禁用手动频繁调用gc
      System.gc()不管用 ,FGC

    • -XX:+PrintGC
      打印出GC信息

    • -XX:+PrintGCDetails
      打印出GC详细信息

    • -XX:+PrintGCCauses
      打印GC和产生的原因

    • -XX:+PrintHeapAtGC

    • -XX:+PrintGCTimeStamps
      打印GC详细时间

    • -XX:+PrintGCApplicationConcurrentTime (低)
      打印应用程序时间

    • -XX:+PrintGCApplicationStoppedTime (低)
      打印暂停时长

    • -XX:+PrintReferenceGC (重要性低)
      记录回收了多少种不同引用类型的引用

    • -verbose:class
      类加载详细过程

    • -XX:+PrintVMOptions
      打印运行时参数

    • -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
      必须会用,打印参数
      在这里插入图片描述

    • -Xloggc:opt/log/gc.log
      设置gc文件

    • -XX:MaxTenuringThreshold
      升代年龄,最大值15,cms默认6,其他默认15

    • 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 …
      这些不建议设置

    Parallel常用参数

    • -XX:SurvivorRatio
    • -XX:PreTenureSizeThreshold
      大对象到底多大
    • -XX:MaxTenuringThreshold
      指定对象经过多少次垃圾回收进入老年代,cms默认6,其他默认15。补充:动态年龄指年轻代占用空间超Survivor的一半时会把年龄最大的放入老年代。
    • -XX:+ParallelGCThreads
      并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
    • -XX:+UseAdaptiveSizePolicy
      自动选择各区大小比例

    CMS常用参数

    • -XX:+UseConcMarkSweepGC
    • -XX:ParallelCMSThreads
      CMS线程数量
    • -XX:CMSInitiatingOccupancyFraction
      使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)
    • -XX:+UseCMSCompactAtFullCollection
      在FGC时进行压缩
    • -XX:CMSFullGCsBeforeCompaction
      多少次FGC之后进行压缩
    • -XX:+CMSClassUnloadingEnabled
    • -XX:CMSInitiatingPermOccupancyFraction
      达到什么比例时进行Perm回收
    • GCTimeRatio
      设置GC时间占用程序运行时间的百分比
    • -XX:MaxGCPauseMillis
      停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代

    G1常用参数

    • -XX:+UseG1GC
    • -XX:MaxGCPauseMillis
      建议值,G1会尝试调整Young区的块数来达到这个值
    • -XX:GCPauseIntervalMillis
      ?GC的间隔时间
    • -XX:+G1HeapRegionSize
      分区大小,建议逐渐增大该值,1 2 4 8 16 32。
      随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长
      ZGC做了改进(动态区块大小)
    • G1NewSizePercent
      新生代最小比例,默认为5%
    • G1MaxNewSizePercent
      新生代最大比例,默认为60%
    • GCTimeRatio
      GC时间建议比例,G1会根据这个值调整堆空间
    • ConcGCThreads
      线程数量
    • InitiatingHeapOccupancyPercent
      启动G1的堆空间占用比例
    • -XX:+InitiatingHeapOccupacyPercent
      默认值45%,当O超过这个值时,启动MixedGC,垃圾回收算法和cms一样

    二、调优

    1、调优前的基础概念:

    1. 吞吐量:用户代码时间 /(用户代码执行时间 + 垃圾回收时间)
    2. 响应时间:STW越短,响应时间越好

    所谓调优,首先确定,追求啥?吞吐量优先,还是响应时间优先?还是在满足一定的响应时间的情况下,要求达到多大的吞吐量…

    吞吐量优先场景:科学计算、数据挖掘。垃圾回收器一般:(PS + PO)

    响应时间优先场景:网站 GUI API 。垃圾回收器一般:(1.8 G1)

    2、什么是调优?

    1. 根据需求进行JVM规划和预调优
    2. 优化运行JVM运行环境(慢,卡顿)
    3. 解决JVM运行过程中出现的各种问题(OOM)

    3、调优,从规划开始

    步骤:

    1. 熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器)

    2. 选择回收器组合

    3. 计算内存需求(经验值 1.5G 16G)
      一般只能算个大概,然后要经过压测

    4. 选定CPU(越高越好)

    5. 设定年代大小、升级年龄

    6. 设定日志参数

      eg: -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause

      解释:-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log:日志文件,一般是系统名+时间;UseGCLogFileRotation:循环使用;XX:NumberOfGCLogFiles有多少个日志文件;XX:GCLogFileSize:每个日志文件大小。可以每天产生一个日志文件。

    7. 观察日志情况

    三、日志详解

    每种垃圾回收器的日志格式是不同的!

    ps日志

    在这里插入图片描述
    用这个参数运行HelloGC

    java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails  HelloGC
    
    • 1

    查看打印日志
    在这里插入图片描述
    在这里插入图片描述
    其中heap dump部分:

    eden space 5632K, 94% used [0x00000000ff980000,0x00000000ffeb3e28,0x00000000fff00000)
                            后面的内存地址指的是,起始地址,使用空间结束地址,整体空间结束地址
    
    • 1
    • 2

    在这里插入图片描述

    CMS

    执行命令:java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.mashibing.jvm.gc.T15_FullGC_Problem01

    [GC (Allocation Failure) [ParNew: 6144K->640K(6144K), 0.0265885 secs] 6585K->2770K(19840K), 0.0268035 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

    ParNew:年轻代收集器

    6144->640:收集前后的对比

    (6144):整个年轻代容量

    6585 -> 2770:整个堆的情况

    (19840):整个堆大小

     [GC (CMS Initial Mark) [1 CMS-initial-mark: 8511K(13696K)] 9866K(19840K), 0.0040321 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]     
     //8511 (13696) : 老年代使用(最大)    
     //9866 (19840) : 整个堆使用(最大)
     [CMS-concurrent-mark-start]
     [CMS-concurrent-mark: 0.018/0.018 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]     
     //这里的时间意义不大,因为是并发执行
     [CMS-concurrent-preclean-start][CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]     
     //标记Card为Dirty,也称为Card Marking
     [GC (CMS Final Remark) [YG occupancy: 1597 K (6144 K)][Rescan (parallel) , 0.0008396 secs][weak refs processing, 0.0000138 secs][class unloading, 0.0005404 secs][scrub symbol table, 0.0006169 secs][scrub string table, 0.0004903 secs][1 CMS-remark: 8511K(13696K)] 10108K(19840K), 0.0039567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]     
     //STW阶段,YG occupancy:年轻代占用及容量   
      //[Rescan (parallel):STW下的存活对象标记    
      //weak refs processing: 弱引用处理   
       //class unloading: 卸载用不到的class    
       //scrub symbol(string) table:         
       //cleaning up symbol and string tables which hold class-level metadata and        
       //internalized string respectively    
       //CMS-remark: 8511K(13696K): 阶段过后的老年代占用及容量    
       //10108K(19840K): 阶段过后的堆占用及容量
       ​[CMS-concurrent-sweep-start]
       [CMS-concurrent-sweep: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]     
       //标记已经完成,进行并发清理
       [CMS-concurrent-reset-start]
       [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]    
       //重置内部结构,为下次GC做准备
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    G1

    [GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs]
    //young -> 年轻代 Evacuation-> 复制存活对象 
    //initial-mark 混合回收的阶段,这里是YGC混合老年代回收
       [Parallel Time: 1.5 ms, GC Workers: 1] //一个GC线程
          [GC Worker Start (ms):  92635.7]
          [Ext Root Scanning (ms):  1.1]
          [Update RS (ms):  0.0]
             [Processed Buffers:  1]
          [Scan RS (ms):  0.0]
          [Code Root Scanning (ms):  0.0]
          [Object Copy (ms):  0.1]
          [Termination (ms):  0.0]
             [Termination Attempts:  1]
          [GC Worker Other (ms):  0.0]
          [GC Worker Total (ms):  1.2]
          [GC Worker End (ms):  92636.9]
       [Code Root Fixup: 0.0 ms]
       [Code Root Purge: 0.0 ms]
       [Clear CT: 0.0 ms]
       [Other: 0.1 ms]
          [Choose CSet: 0.0 ms]
          [Ref Proc: 0.0 ms]
          [Ref Enq: 0.0 ms]
          [Redirty Cards: 0.0 ms]
          [Humongous Register: 0.0 ms]
          [Humongous Reclaim: 0.0 ms]
          [Free CSet: 0.0 ms]
       [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)]
     [Times: user=0.00 sys=0.00, real=0.00 secs] 
    //以下是混合回收其他阶段
    [GC concurrent-root-region-scan-start]
    [GC concurrent-root-region-scan-end, 0.0000078 secs]
    [GC concurrent-mark-start]
    //无法evacuation,进行FGC
    [Full GC (Allocation Failure)  18M->18M(20M), 0.0719656 secs]
       [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38
    76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、案例分析

    需要调优的情况一般是运维团队首先受到报警信息(CPU Memory)

    案例1

    试验用程序:

    import java.util.List;
    import java.util.LinkedList;
    
    public class HelloGC {
      public static void main(String[] args) {
        System.out.println("HelloGC!");
        List list = new LinkedList();
        for(;;) {
          byte[] b = new byte[1024*1024];
          list.add(b);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 区分概念:内存泄漏memory leak,内存溢出out of memory

    2. java -XX:+PrintCommandLineFlags HelloGC

    3. java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC

    (相关参数含义查看上文)
    在这里插入图片描述
    GC默认就是YGC,清理年轻代
    (打印信息没截完)

    1. java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
      在这里插入图片描述
      在这里插入图片描述
      (打印信息没能截完)

      用UseConcMarkSweepGC 垃圾回收次数会多很多

    2. java -XX:+PrintFlagsInitial 默认参数值

    3. java -XX:+PrintFlagsFinal 最终参数值

    4. java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数

    5. java -XX:+PrintFlagsFinal -version |grep GC

    案例2

    1、代码

    import java.math.BigDecimal;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输
     */
    
    public class T15_FullGC_Problem01 {
    
        private static class CardInfo {
            BigDecimal price = new BigDecimal(0.0);
            String name = "张三";
            int age = 5;
            Date birthdate = new Date();
    
            public void m() {}
        }
    
        private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
                new ThreadPoolExecutor.DiscardOldestPolicy());
    
        public static void main(String[] args) throws Exception {
            executor.setMaximumPoolSize(50);
    
            for (;;){
                modelFit();
                Thread.sleep(100);
            }
        }
    
        private static void modelFit(){
            List taskList = getAllCardInfo();
            taskList.forEach(info -> {
                // do something
                executor.scheduleWithFixedDelay(() -> {
                    //do sth with info
                    info.m();
    
                }, 2, 3, TimeUnit.SECONDS);
            });
        }
    
        private static List getAllCardInfo(){
            List taskList = new ArrayList<>();
    
            for (int i = 0; i < 100; i++) {
                CardInfo ci = new CardInfo();
                taskList.add(ci);
            }
    
            return taskList;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    2、运行程序:java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.gc.T15_FullGC_Problem01
    3、出现报警信息后,top命令观察到问题:内存不断增长 CPU占用率居高不下
    在这里插入图片描述
    (环境问题,假如Java进程出问题了,且pid是16222)
    4、top -Hp 观察进程中的线程,哪个线程CPU和内存占比高
    在这里插入图片描述
    5、jps定位具体java进程
    6、jstack 定位线程状况,重点关注:WAITING BLOCKED

    这里Java进程有问题,且pid是1591(环境一致的话上面top命令能查出占用资源多的pid是1591)
    在这里插入图片描述
    waiting on <0x0000000088ca3310> (a java.lang.Object)
    说明出现锁等待了

    这里就能说明为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称,有意义的名称能快速定位问题。

    6、jinfo pid 打印进程的相关信息
    在这里插入图片描述
    (没截全)

    7、jstat -gc 动态观察gc情况
    jstat -gc 4655 500 : 每个500个毫秒打印GC的情况
    在这里插入图片描述
    阅读GC日志发现频繁GC,查看GC dump日志工具:
    arthas观察 / jconsole/jvisualVM/ Jprofiler(收费,最好用)

    8、jmap - histo 4655 | head -20,查找有多少对象产生,查出前20个(面试时可以说用这个)
    在这里插入图片描述
    9、jmap -dump:format=b,file=xxx pid :(面试时不要说用这个)

    线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)

    ​ 1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件
    ​ 2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
    ​ 3:在线定位(一般小点儿公司用不到)

    除了使用jmap命令,还可以通过以下方式:

    1. 使用 jconsole 选项通过 HotSpotDiagnosticMXBean 从运行时获得堆转储(生成dump文件)
    2. 虚拟机启动时如果指定了 -XX:+HeapDumpOnOutOfMemoryError 选项, 则在抛出OutOfMemoryError 时, 会自动执行堆转储。如:java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem01
    3. 使用 hprof 命令

    10、获取到dump文件之后可以使用MAT / jhat /jvisualvm 进行dump文件分析,参考这博客:https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
    使用 jhat -J-mx512M xxx.dump 解析后,访问 http://ip:7000,访问分析dump文件,
    可以使用OQL查找特定问题对象

    11、找到代码的问题

  • 相关阅读:
    C++ opencv设置视频的捕获方式为 MJPG设置失败
    区间dp
    图像数据增强2_标注框同时修改(VOC、YOLO)
    92.(cesium之家)cesium楼栋分层
    【Java】OpenPDF、iText、PDFBox 是三种常用的 PDF 处理库
    武汉理工大学 Python程序设计第五章测验
    CGI与FastCGI的区别在哪里,FastCGI的应用场景讲解
    Jmeter性能测试 —— TPS拐点寻找
    SpringCloud Fegin CircuitBreaker (原hystrix) 熔断失效原因
    python爬虫1
  • 原文地址:https://blog.csdn.net/w1lgy/article/details/126496422