0
点赞
收藏
分享

微信扫一扫

JVM第七课:实战理解GC调优

you的日常 2022-01-17 阅读 65

GC常用参数

  • -Xmn -Xms -Xmx -Xss
    年轻代 最小堆 最大堆 栈空间大小
  • -XX:+UseTLAB
    使用TLAB,默认打开
  • -XX:+PrintTLAB
    打印TLAB的使用情况
  • -XX:TLABSize
    设置TLAB大小
  • -XX:+DisableExplictGC
    System.gc()不管用 ,FGC
  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintHeapAtGC
  • -XX:+PrintGCTimeStamps
  • -XX:+PrintGCApplicationConcurrentTime (低)
    打印应用程序时间
  • -XX:+PrintGCApplicationStoppedTime (低)
    打印暂停时长
  • -XX:+PrintReferenceGC (重要性低)
    记录回收了多少种不同引用类型的引用
  • -verbose:class
    类加载详细过程
  • -XX:+PrintVMOptions
  • -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
    必须会用
  • -Xloggc:opt/log/gc.log
  • -XX:MaxTenuringThreshold
    升代年龄,最大值15
  • 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 …
    这些不建议设置

JVM调优第一步:了解JVM常用命令行参数

public class HelloGC {
    public static void main(String[] args) {
        System.out.println("hello gc~");
        List<byte[]> list = new LinkedList<>();
        for (; ; ) {
            list.add(new byte[1024 * 1024]);
        }
    }
}

区分概念:内存泄漏memory leak,内存溢出out of memory

内存泄漏就是有一块内存,我们用不到 但是GC也无法回收
内存溢出是分配内存时,发现内存不够用啦

这俩其实没啥必然的联系:
泄漏不一定导致溢出,内存总空间够大的话,浪费一点也不致命;
内存溢出不一定是由内存泄漏导致,内存总空间比较小,程序的对象又很多时,正常的使用也可能内存溢出

-XX:+PrintCommandLineFlags

输出JVM的默认参数:

-Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC

  • -Xmn 年轻代空间的最大值,-XX:MaxNewSize
  • -Xms 堆空间的最小值,-XX:InitialHeapSize,即初始大小
  • -Xmx 堆空间的最大值,-XX:MaxHeapSize
    一般把Xms和Xmx设置成一样的大小,避免堆的扩容和缩容所带来的资源消耗
  • -XX:+PrintGC 打印GC信息
    关于打印GC的参数还有:
    -XX:+PrintGCDetails 打印详细信息,这个下面详细看
    -XX:+PrintGCTimeStamps 时间戳
    -XX:+PrintGCCause 原因

执行后输出:
GC就是Young GC,年轻代的GC

-XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -XX:+PrintGC

-XX:+UseConcMarkSweepGC 使用CMS,观察CMS的GC
输出相比默认垃圾回收期PS+PO会更为详尽,
并且多了CMS Initial Mark

GC日志格式

在这里插入图片描述
关于上图中的Times含义:在linux中的times代表:
在这里插入图片描述

Heap Dump的含义(Linux)

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

年轻代的total = eden + 1个survivor
在这里插入图片描述

调优前的基础概念

  1. 吞吐量throughput:用户代码执行时间 /(用户代码执行时间 + 垃圾回收时间)
    (可以理解为干正经事的时间占总时间的比例)
    吞吐量越大,JVM花在GC上的时间越少
  2. 响应时间:STW越短,响应时间越好
    这俩指标几乎是不可能兼得的,
    想要更高的吞吐量,就是要GC总体占用更少的CPU时间,这样的话,一旦需要STW,会STW很久(典型代表:CMS);
    想要更快的响应时间,就是要STW很短,需要有线程去不断的标记处理对象,但是这样的话,吞吐量势必会降低,因为GC占用更多的CPU时间了.

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

问题:
吞吐量优先:科学计算/数据挖掘,一般选择PS + PO
响应时间优先:网站 GUI API (1.8 选择G1)

什么是调优

  1. 根据需求进行JVM规划和预调优
  2. 优化运行JVM环境(慢,卡顿,一般是GC的STW过长了)
  3. 解决JVM运行过程中出现的各种问题(CPU 内存使用过高告警,OOM)

并发理解:

  • QPS
  • TPS
    淘宝双11并发历年最高54万,据说12306并发比淘宝更高,号称上百万

调优,从规划开始

  • 调优,从业务场景开始,没有业务场景的调优都是耍流氓
  • 无监控(压力测试,能看到结果),不调优
  • 步骤:
  1. 熟悉业务场景,选择优化的维度(没有最好的垃圾回收器,只有最合适的垃圾回收器)
    1. 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
    2. 吞吐量 = 用户时间 /( 用户时间 + GC时间) [PS]
  2. 选择回收器组合
  3. 计算内存需求(经验值,不一定是越大越好, 有的从1.5G升到16G甚至还慢了很多)
  4. 选定CPU(越高越好)
  5. 设定年代大小、升级年龄
  6. 设定日志参数
    1. -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5
      -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
      使用5个GC日志文件,每个文件最大20M,超过5个时最新的文件会把最老的覆盖掉
    2. 或者每天产生一个日志文件
  7. 观察日志情况
  • 案例1:垂直电商,最高每日百万订单,处理订单系统需要什么样的服务器配置?

  • 案例2:12306遭遇春节大规模抢票应该如何支撑?(架构问题)

  • 怎么得到一个事务会消耗多少内存?

优化环境

  1. 有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器32位,1.5G的堆,用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了

    1. 为什么原网站慢?
      很多用户浏览数据,很多数据load到内存,内存不足,频繁GC,STW长,响应时间变慢
    2. 为什么会更卡顿?
      内存越大,FGC时间越长
    3. 咋办?
      PS -> PN + CMS 或者 G1
  2. 系统CPU经常100%,如何调优?(面试高频)
    CPU100%那么一定有线程在占用系统资源,

    1. 找出哪个进程cpu高(top)
    2. 该进程中的哪个线程cpu高(top -Hp)
    3. 导出该线程的堆栈 (jstack)
    4. 查找哪个方法(栈帧)消耗时间 (jstack)
      工作线程占比高 | 垃圾回收线程占比高
  3. 系统内存飙高,如何查找问题?(面试高频

    1. 导出堆内存 (jmap)
    2. 分析 (jhat jvisualvm mat jprofiler … )
  4. 如何监控JVM

    1. jstat jvisualvm jprofiler arthas top…
举报

相关推荐

0 条评论