JVM调优学习

 

  • 写在前面:
  1. 系统优化不只有JVM调优一个角度, 也许其他方式适合你的使用;
  2. 性能调优的常见方式:
    1. JVM调优: 垃圾收集, 内存分配;
    2. 架构调优: 评估整体架构的合理性, 扩展性, 各方面可能存在的问题;
    3. 代码调优: 算法和数据结构的灵活使用;
    4. 数据库调优: 数据表的设计和分配, SQL优化;
    5. 外部因素: 带宽, 负载均衡限制, 甚至是操作系统等;
  3. 把架构优化和代码优化放在JVM优化之前, 找到你的性能优化目标, 找到瓶颈, 压力测试, 通过监控和统计工具得出结论;
  • 一步步走:
  1. 调优目标:
    1. 一般是以更高的吞吐量, 更低的延迟, 更低的内存占用做为JVM调优的目标;
  2. 性能定义:
    1. 吞吐量: 是指不考虑垃圾收集引起的停顿时间或内存消耗, 垃圾收集器能支撑应用达到的最高性能指标;
    2. 延迟: 其度量标准是缩短由于垃圾回收引起的停顿时间或者完全消除因垃圾收集所引起的停顿, 避免应用运行时发生抖动;
    3. 内存占用: 垃圾收集器流畅运行所需要的内存大小;
  3. 调优原则:
    1. MinorGC回收原则: 每次MinorGC尽可能多的收集垃圾对象, 以减少FullGC的频率;
    2. GC内存最大化原则: 处理吞吐量和延迟问题的时候, 垃圾处理器能使用的内存越大, 垃圾收集的效果越好, 程序更流畅;
    3. GC调优3选2原则: 性能属性中, 很难做到三者兼得, 一般重吞吐量和延迟优化, 轻内存占用优化;
  4. 垃圾处理器选择:
    1. 根据当前机器来看默认选用[java -XX:+PrintCommandLineFlags -version], jdk1.8使用的是[-XX:+UseParallelGC], jdk11使用的是[-XX:+UseG1GC], G1处理器需求更大的堆内存(>=6G), 可保持较高的吞吐量并将GC延迟降到足够低(<500ms);
  5. 应用程序的运行阶段:
    1. 初始化阶段: JVM加载应用程序, 初始化应用程序的主要模块和数据;
    2. 稳定阶段: 应用在此时运行了大多数时间, 经历过压力测试的之后, 各项性能参数呈稳定状态; 核心函数被执行, 已经被jit编译预热过;
    3. 总结阶段: 最后的总结阶段, 进行一些基准测试, 生成响应的策报告(这个阶段我们不关注);
  6. 简略流程:
    1. 确定性能指标;
    2. JVM运行;
    3. 确定内存占用;
    4. 内存是否正常?
    5. 确定程序延迟;
    6. 延迟是否正常?
    7. 确定程序吞吐量;
    8. 吞吐量是否正常?
    9. 当上面每一次得不到想要的结果时执行调优, 顺序不要变;
  7. 内存分配;
    1. 新生代, 老年代, 元空间;
    2. 可指定的值:
      1. 空间 参数  说明
        -XX:MaxHeapSize | -Xmx 最大堆容量;
          -XX:InitialHeapSize | -Xms 设置堆的初始大小; 默认空余堆内存>70%时JVM会减少堆到-Xms的大小;
        元空间 -XX:MaxMetaspaceSize 最大元空间容量;
          -XX:MetaspaceSize 设置分配的类元数据空间的大小(建议一次到位);
        新生代 -XX:NewSize 新生代的初始大小, 建议堆的1/4到1/2; 默认根据堆大小,核数, 机器位数计算得到;
          -XX:MaxNewSize 新生代的最大大小; 
          -Xmn  新生代初始和最大大小=同时设置上面两个值;
        -XX:ThreadStackSize | -Xss 设置线程堆栈大小(减少栈大小可以提高运行线程数);
        老年代 -XX:NewRatio 老年代与年轻代的比, 默认为2; 
            初始值等于-Xmx减-XX:NewSize
            最小值等于-Xmx减-XX:MaxNewSize

 

 

 

 

 

 

 

 

 

 

 

 

 

  1. GC日志:
    1. 命令设置:
      1. -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:<filename>
    1. 得到信息:
      1. [Full GC (Metadata GC Threshold) 
        [PSYoungGen: 10290K->0K(161792K)] 
        新生代: GC前->GC后 新生代大小
        [ParOldGen: 112K->10175K(175104K)] 
        老年代: GC前->GC后 老年代大小
        10402K->10175K(336896K), 
        堆: GC前->GC后 堆大小
        [Metaspace: 20497K->20495K(1067008K)], 
        元空间: GC前->GC后 元空间大小
         0.0374949 secs] 
         FullGC时间
        [Times: user=0.11 sys=0.01, real=0.04 secs] 
  1. 建议大小:
    1. 空间 命令 建议
      -Xms -Xmx 3-4倍FullGC后的老年代空间占用, 最大最小相同
      元空间 -XX:MetaspaceSize -XX:MaxMetaspaceSize 初始值为1.2-1.5FullGC后的元空间占用
      新生代 -Xmn 1-1.5倍FullGC后的老年代占用
      老年代   2-3倍FullGC后的老年代占用



 

 

 

  1. 延迟调优:
    1. 可执行的操作有: 继续调整内存分配, 或者切换其他垃圾处理器;
    2. 排除掉代码和SQL导致的延迟后, JVM的可调优指标有:
      1. MinorGC的时间;
      2. MinorGC的频率;
      3. FullGC的时间(最差情况);
      4. FullGC的频率(最差情况);
      5. 上面MinorGC的频率和FullGC的时间影响偏大;
    3. 普遍指标(生产环境):
      1. MinorGC时间<50ms(维持在10ms左右), 频率在10s以上;
      2. FullGC时间<500ms, 频率在10min以上;
    4. 调优方向:
      1. 降低MinorGC时间: 减小新生代大小, 提高内存分页大小;
      2. 降低MinorGC的频率: 提交新生代大小;
      3. 降低FullGC频率: 提高老年代大小, 提高新生代晋升岁数, 提高新生代大小;
      4. 降低FullGC时间: 降低老年代大小, 提高内存分页大小, 切换G1处理器;
    5. MinorGC日志计算老年代大小:
      1. MinorGC会输出堆大小变化, 得到每次MinorGC后老年代大小, 即晋升的数据大小;
      2. 次数=老年代大小/晋升数据平均大小;
      3. 计算出多久老年代会被MinorGC填满 = MinorGC频率 * 次数;
  2. 吞吐量调优:
    1. 尽可能避免或者很少发生FullGC 或者CMS压缩式垃圾收集, 因为这两种方式都会造成应用程序吞吐降低;
    2. 尽量在MinorGC 阶段回收更多的对象, 避免对象提升过快到老年代;
    3. 使用G1处理器;
  3. 内存优化:
    1. -Xss调整栈大小, 可以减少内存占用, 提高并发线程数;

 

  • PS: 被表格和代码块打乱了序号...



 

posted @ 2021-02-22 11:53  付二十  阅读(43)  评论(0编辑  收藏  举报