基础概念

  • 吞吐量:用户代码时间 / (用户代码执行时间+GC时间),科学计算、数据挖掘…吞吐量优先(PS+PO)
  • 响应时间:GC过程中的STW时间越短,响应时间越快 ,网站GUI、API…响应时间优先(G1、CMS+ParNew、ZGC)

一. 调优的目的

  1. 根据需求进行JVM规划和预调优
  2. 优化JVM运行环境:慢、卡
  3. 解决JVM运行中的各种问题:OOM…

二. 步骤

  1. 熟悉业务场景:吞吐量、响应时间
  2. 选择最合适的垃圾回收器组合

垃圾回收器跟内存的关系:

  • Serial:几十MB
  • PS:几百兆-几个G
  • CMS:20G
  • G1:上百G
  • ZGC:4T-16T
  • jdk1.8 default:PS+PO
  1. 计算内存需求
  2. 选定CPU(越高越好啊)
  3. 设定年代大小、升级年龄
  4. 设定日志参数

-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UserGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
或者一天生成一个日志文件

  1. 观察日志情况

三. 热点问题

1. 系统CPU经常100%,如何查找问题?
一定是有线程占用资源(工作线程或者GC线程)

  1. 找出哪个进程CPU占用高:top
  2. 找出该进程中哪个线程CPU占用高:top -Hp
  3. 导出该线程的堆栈:jstack
  4. 查找哪个方法(栈帧)消耗时间: stack

2. 系统内存高,如何查找问题?

  1. 导出堆内存:jmap
  2. 分析:jhat jvisualvm mat jprofiler

四. 实验

1. 实验环境

CentOS6,JDK1.8,内存4G

2. 测试代码
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<CardInfo> taskList = getAllCardInfo();
        taskList.forEach(info -> {
            // do something
            executor.scheduleWithFixedDelay(() -> {
                //do sth with info
                info.m();

            }, 2, 3, TimeUnit.SECONDS);
        });
    }

    private static List<CardInfo> getAllCardInfo(){
        List<CardInfo> taskList = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
            CardInfo ci = new CardInfo();
            taskList.add(ci);
        }

        return taskList;
    }
}
3. 测试过程

先用javac编译然后执行

javac T15_FullGC_Problem01.java
java -Xms200M -Xmx200M -XX:+PrintGC T15_FullGC_Problem01

运行一段时间后发现FullGC频繁,一定是产生了垃圾回收不掉

java数据吞吐量是什么意思 jvm吞吐量和响应时间_List

使用top查看占用CPU最高的进程ID为2532

java数据吞吐量是什么意思 jvm吞吐量和响应时间_java数据吞吐量是什么意思_02


使用top -Hp 2532查看占用CPU最高的线程ID为2534

java数据吞吐量是什么意思 jvm吞吐量和响应时间_List_03


使用jstack查看进程dump的信息,关注WAITING BLOCK这样的字段(配合grep使用更舒服),看看有没有多个线程在等一把锁,如果有,可能是死锁了,这里并没有发现死锁

java数据吞吐量是什么意思 jvm吞吐量和响应时间_java_04

使用jmap -histo查看占用堆内存最多的对象,发现CardInfo对象非常滴多!

  • 如果是线上系统,谨慎使用jmap!会对程序的影响很大!
  • jmap -dump:format=b,file=xxx pid 设定了参数HeapDump,OOM的时候会自动产生堆转储文件
  • 或者使用在线定位

找到代码的问题是cardinfo对象太多了,想想办法解决一下

其他常用指令/工具

  1. jinfo:列出jvm的一些状态
  2. java数据吞吐量是什么意思 jvm吞吐量和响应时间_java_05

  3. jstat:查看GC的一些信息,可以设置interval
  4. java数据吞吐量是什么意思 jvm吞吐量和响应时间_响应时间_06

  5. JConsole:在远程连接服务器,可以通过窗口调试
    自行了解 类似的还有jvisualvm、jprofiler等
  6. Arthars:Alibaba开源的Java诊断工具
    https://arthas.aliyun.com/doc/