JVM调优

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

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

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

  • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old

  • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 

  • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old (跟上面一样)

  • -XX:+UseG1GC = G1

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

    • java +XX:+PrintCommandLineFlags -version
    • 通过GC的日志来分辨

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

查看-XX 关于CMS的有哪些参数:java -XX:+PrintFlagsFianl -version | CMS

实验程序:

public class HelloGC {
  public static void main(String[] args) {
    System.out.println("HelloGC!");
    List list = new LinkedList();
    for(;;) {
      byte[] b = new byte[1024*1024]; //1M
      list.add(b);
    }
  }
}
  1. 区分概念:内存泄漏memory leak,内存溢出out of memory
  2. java -XX:+PrintCommandLineFlags HelloGC
  3. java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC (PrintGCDetails)(PrintGCTimeStamps )(PrintGCCauses)HelloGC
  4. java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
  5. java -XX:+PrintFlagsInitial 默认参数值
  6. java -XX:+PrintFlagsFinal 最终参数值
  7. java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数
  8. java -XX:+PrintFlagsFinal -version |grep GC

-Xmn:新生代的大小    Xms:最小堆的大小。Xmx:最大堆的大小。(Xms和Xmx最好设置一样大小)

 PrintGC:打印GC回收的信息。

PrintGCDetails:打印GC更详细的信息

PrintGCTimeStamps :打印GC产生的时候的系统时间

PrintGCCauses:打印GC所产生的原因

PS GC日志详解

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

PS日志格式

heap dump部分:

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

total = eden + 1个survivor

调优前的基础概念:

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

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

如:科学计算,数据挖掘。(吞吐量优先)吞吐量优先的一般选:(PS + PO)

如:网站   GUI   API (响应时间优先)(1.8 的话尽量选G1)

什么是调优?

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

调优,从规划开始

  • 调优,从业务场景开始,没有业务场景的调优都是耍流氓

  • 无监控(压力测试,能看到结果),不调优

  • 步骤:

    1. 熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器)
      1. 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
      2. 吞吐量 = 用户时间 /( 用户时间 + GC时间) [PS]
    2. 选择回收器组合
    3. 计算内存需求(经验值)
    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
      2. 或者每天产生一个日志文件
    7. 观察日志情况

xxx-xxx-gc-%t.log :日志名,-%t 系统时间

-XX:+UseGCLogFileRotation:循环使用

-XX:NumberOfGCLogFiles=5:5个日志文件

-XX:GCLogFileSize=20M:每个日志文件的大小20M

(5个日志文件循环产生,每个日志满了之后覆盖最前面的)

案例1:垂直电商,最高每日百万订单,处理订单系统需要什么样的服务器配置?

这个问题比较业余,因为很多不同的服务器配置都能支撑(1.5G 16G)

1小时360000集中时间段, 100个订单/秒,(找一小时内的高峰期,1000订单/秒)

经验值,

非要计算:一个订单产生需要多少内存?512K * 1000 500M内存

专业一点儿问法:要求响应时间100ms

压测!

案例2:12306遭遇春节大规模抢票应该如何支撑?

12306应该是中国并发量最大的秒杀网站:

号称并发量100W最高

CDN -> LVS -> NGINX -> 业务系统 -> 每台机器1W并发(10K问题) 100台机器

普通电商订单 -> 下单 ->订单系统(IO)减库存 ->等待用户付款

12306的一种可能的模型: 下单 -> 减库存 和 订单(redis kafka) 同时异步进行 ->等付款

减库存最后还会把压力压到一台服务器

可以做分布式本地库存 + 单独服务器做库存均衡

大流量的处理方法:分而治之

优化环境

  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)PID(进程pid)
    2. 该进程中的哪个线程cpu高(top -Hp  PID(进程pid))
    3. 导出该线程的堆栈 (jstack PID(线程pid)命令定位到问题具体在哪一行)
    4. 查找哪个方法(栈帧)消耗时间 (jstack)
    5. 工作线程占比高 | 垃圾回收线程占比高
  3. 系统内存飙高,如何查找问题?(面试高频)
    1. 导出堆内存 (jmap) jmap -dump:format=b,file=dump本地(文件名) 2254(java的pid)
    2. 分析 (jhat jvisualvm mat jprofiler ... )
  4. 如何监控JVM
    1. jstat jvisualvm jprofiler arthas top...

解决JVM运行中的问题

一个案例理解常用工具

  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<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;
    }
    }

  2. java -Xms200M -Xmx200M -XX:+PrintGC 应用程序名称

  3. 一般是运维团队首先受到报警信息(CPU Memory)

  4. top命令观察到问题:内存不断增长 CPU占用率居高不下

  5. top -Hp 观察进程中的线程,哪个线程CPU和内存占比高

  6. jps定位具体java进程 ,jstack 定位线程状况,重点关注:WAITING, BLOCKED 状态的 eg. waiting on <0x0000000088ca3310> (a java.lang.Object)。 假如有一个进程中100个线程,很多线程都在waiting on ,一定要找到是哪个线程持有这把锁 ,怎么找?搜索jstack dump的信息,找 ,看哪个线程持有这把锁RUNNABLE 

  7. 为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称 怎么样自定义线程池里的线程名称?(自定义ThreadFactory)

  8. jinfo pid

  9. jstat -gc 动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole/jvisualVM/ Jprofiler(最好用,收费) jstat -gc 4655 500 : 每个500个毫秒打印GC的情况 如果面试官问你是怎么定位OOM问题的?如果你回答用图形界面(错误) 1:已经上线的系统不用图形界面用什么?(cmdline arthas) 2:图形界面到底用在什么地方?测试!测试的时候进行监控!(压测观察)

  10. jmap - histo 4655 | head -20,查找有多少对象产生(频繁GC时,用jmap查看哪些对象的实例最多)

  11. jmap -dump:format=b,file=xxx pid :

    线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合) 1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件 2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响 3:在线定位(一般小点儿公司用不到)

  12. java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError 应用程序名称(设定了参数HeapDump,OOM的时候会自动产生堆转储文件)

  13. 使用MAT / jhat /jvisualvm 进行dump文件分析 java命令--jhat命令使用 - 白灰 - 博客园 jhat -J-mx512M xxx.dump http://192.168.17.11:7000 拉到最后:找到对应链接 可以使用OQL查找特定问题对象

  14. 找到代码的问题

jconsole远程连接

  1. 程序启动加入参数:

    java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false XXX
  2. 如果遭遇 Local host name unknown:XXX的错误,修改/etc/hosts文件,把XXX加入进去

    192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
  3. 关闭linux防火墙(实战中应该打开对应端口)

    service iptables stop
    chkconfig iptables off #永久关闭
  4. windows上打开 jconsole远程连接 192.168.17.11:11111

jvisualvm远程连接

使用jvisualvm的jstatd方式远程监控Java程序 - 静若清池 - 博客园 (简单做法)

arthas在线排查工具

  • 为什么需要在线排查? 在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因。为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布,如果打了日志还是没找到问题,继续加日志,重新打包发布。对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度。
  • jvm观察jvm信息
  • thread定位线程问题
  • dashboard 观察系统情况
  • heapdump + jhat分析
  • jad反编译 动态代理生成类的问题定位 第三方的类(观察代码) 版本问题(确定自己最新提交的版本是不是被使用)
  • redefine 热替换 目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性 m() -> mm()
  • sc - search class
  • watch - watch method
  • 没有包含的功能:jmap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值