Jvm 调优

YC-L 2021-02-20 01:39:00
原文地址:https://www.cnblogs.com/YC-L/p/14415365.html

Jvm 常用命令行参数

JVM的命令行参数参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

HotSpot参数分类

  • 标准: - 开头,所有的HotSpot都支持
  • 非标准:-X 开头,特定版本HotSpot支持特定命令
  • 不稳定:-XX 开头,下个版本可能取消

试验用程序

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

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

  • 内存泄漏,指针丢失
  • 内存溢出,内存爆满

java常用命令行参数

  • java -XX:+PrintCommandLineFlags HelloGC
  • java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC PrintGCDetails PrintGCTimeStamps PrintGCCauses
  • Xms:最小堆大小,Xmx:最大堆大小,一般这俩值大小一样,Xmn:新生代的大小
  • java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
  • java -XX:+PrintFlagsInitial 默认参数值
  • java -XX:+PrintFlagsFinal 最终参数值
  • java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数
  • java -XX:+PrintFlagsFinal -version | grep GC

PS GC日志详解

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

PS日志格式

![]()

heap dump部分

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

![]()

total = eden + 1个survivor

调优前的基础概念

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

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

  • 吞吐量优先,科学计算。数据挖掘,throuput。吞吐量优先的一般:(PS + PO)
  • 响应时间优先:网站 GUI API JDK1.8 选G1,也可以选PN + CMS

什么是调优?

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

调优,从规划开始

  • 调优,从业务场景开始,没有业务场景的调优都是扯淡
  • 无监控(无压力测试,看不到结果),不调优

步骤

  • 熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器)
  • 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
  • 吞吐量 = 用户时间 /( 用户时间 + GC时间) [PS]
  • 选择回收器组合
  • 计算内存需求(经验值 1.5G 16G)
  • 选定CPU(越高越好)
  • 设定年代大小、升级年龄
  • 设定日志参数
  • -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
  • 或者每天产生一个日志文件
  • 观察日志情况

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

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

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

经验值,一个订单产生需要多少内存?512K * 1000 500M内存

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

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

12306应该是中国并发量最大的秒杀网站:号称并发量100W最高

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

  • 普通电商订单 -> 下单 ->订单系统(IO)减库存 ->等待用户付款
  • 12306的一种可能的模型: 下单 -> 减库存 和 订单(redis kafka) 同时异步进行 ->等付款

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

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

大流量的处理方法:分治

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

  • 弄台机器,看能承受多少TPS?是不是达到目标?
  • 扩容或调优,让它达到用压测来确定

优化环境

有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器32位,1.5G的堆

用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G 的堆内存

结果用户反馈卡顿十分严重,反而比以前效率更低了

为什么原网站慢?

很多用户浏览数据,很多数据load到内存,内存不足,频繁GC,STW长,响应时间变慢

为什么会更卡顿?

内存越大,FGC时间越长,咋办?

  • PS -> PN + CMS 或者 G1

系统CPU经常100%,如何调优?(面试高频)

  • CPU100%那么一定有线程在占用系统资源
  • 找出哪个进程cpu高(top)
  • 该进程中的哪个线程cpu高(top -Hp)
  • 导出该线程的堆栈 (jstack)
  • 查找哪个方法(栈帧)消耗时间 (jstack)

工作线程占比高 | 垃圾回收线程占比高

系统内存飙高,如何查找问题?(面试高频)

  • 导出堆内存 (jmap)
  • 分析 (jhat jvisualvm mat jprofiler ... )

如何监控JVM

  • jstat(不重要)
  • jvisualvm
  • jprofiler(收费,用得少)
  • arthas(大公司用,阿里出品)
  • top

解决JVM运行中的问题

一个案例理解常用工具

测试代码:

package com.test.jvm.gc;

import java.math.BigDecimal;<br></br>import java.util.ArrayList;<br></br>import java.util.Date;<br></br>import java.util.List;<br></br>import java.util.concurrent.ScheduledThreadPoolExecutor;<br></br>import java.util.concurrent.ThreadPoolExecutor;<br></br>import java.util.concurrent.TimeUnit;

/** * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输 */

public class T15FullGCProblem01 {

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

跑跑程序

java -Xms200M -Xmx200M -XX:+PrintGC com.test.jvm.gc.T15FullGCProblem01

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

top 命令观察到问题

  • 内存不断增长
  • CPU占用率居高不下

top -Hp pid

  • 观察进程中的线程,哪个线程CPU和内存占比高

jps定位具体java进程

jstack pid 定位线程状况

  • 重点关注:WAITING BLOCKED
  • eg. waiting on <0x0000000088ca3310> (a java.lang.Object)

![]()

假如有一个进程中100个线程,很多线程都在waiting on,一定要找到是哪个线程持有这把锁,怎么找?

  • 搜索 jstack dump 的信息,找看哪个线程持有这把锁RUNNABLE

为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称

出了问题容易定位

怎么样自定义线程池里的线程名称

自定义ThreadFactory

jinfo pid

jstat -gc pid

动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole / jvisualVM / Jprofiler(最好用)

jstat -gc 4655

500 : 每个500个毫秒打印GC的情况

面试官问怎么定位OOM问题

  • 如果你回答用图形界面(错误)
  • 已经上线的系统不用图形界面用什么?(cmd line arthas)

图形界面到底用在什么地方

测试,测试的时候进行监控!(压测观察)

动态图的一张截图,查找前面20个(histogram),执行该命令对在线系统略有影响

jmap - histo pid | head -20

手动导出堆转储文件,执行该命令对在线系统影响非常高

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

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

  • 设定了参数HeapDump,OOM的时候会自动产生堆转储文件
  • 很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
  • 在线定位,arthas(一般小公司用不到)
java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError

com.test.jvm.gc.T15FullGCProblem01

jconsole远程连接

![]()

程序启动加入参数

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

``如果遭遇 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

``关闭linux防火墙(实战中应该打开对应端口)

service iptables stop
chkconfig iptables off // 永久关闭

windows上打开 jconsole远程连接 192.168.17.11:11111

jvisualvm远程连接

https://www.cnblogs.com/YC-L/p/14418763.html

![]()

jprofiler (收费)

arthas在线排查工具(进大公司前不要提,没有提供jmap功能)

github地址:https://github.com/alibaba/arthas/blob/master/README_CN.md

使用arthas-boot(推荐)

下载arthas-boot.jar,然后用java -jar的方式启动

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

打印帮助信息

java -jar arthas-boot.jar -h

如果下载速度比较慢,可以使用aliyun的镜像

java -jar arthas-boot.jar --repo-mirror aliyun --use-http

使用as.sh

Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 Enter 执行即可

curl -L https://arthas.aliyun.com/install.sh | sh
  • 上述命令会下载启动脚本文件 as.sh 到当前目录,你可以放在任何地方或将其加入到 $PATH
  • 直接在shell下面执行./as.sh,就会进入交互界面
  • 也可以执行./as.sh -h来获取更多参数信息

启动arthas

java -jar arthas-boot.jar

选择想要检查的进程编号,artha挂载(attach)到进程上

![]()

常用命令

jvm观察jvm信息(类似jinfo),thread定位线程问题

dashboard 观察系统情况(类似于top命令)

heapdump(相当于jmap -dump) + jhat分析(-J 参数是 maxmum heap size)

heapdump /root/1.hprof

jhat -J-max512M /root/1.hprof

![]()

访问7000端口可以得到分析结果

可以使用jdk1.8的JVM监控工具jhat中的OQL功能查找特定问题对象

使用MAT / jhat /jvisualvm 进行dump文件分析

https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html

jhat -J-mx512M xxx.dump http://192.168.17.11:7000

使用jvisualvm分析dump文件

远程连接主机,打开导出的dump文件

arthas反编译和热替换

jad反编译

  • 动态代理生成类的问题定位
  • 第三方的类(观察代码)
  • 版本问题(确定自己最新提交的版本是不是被使用)

redefine 热替换

线上运行的类出了问题,先启动arthas,然后挂载到该进程,vim修改java文件,然后执行下面的redefine指令

redefine /root/TT.class

目前有些限制条件

  • 只能改方法实现(方法已经运行完成)
  • 不能改方法名
  • 不能改属性 m() -> mm()

sc - search class

watch - watch method

没有包含的功能:jmap

为什么需要在线排查

  • 在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因
  • 为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布
  • 如果打了日志还是没找到问题,继续加日志,重新打包发布
  • 对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度

声明:该文章系转载,转载该文章的目的在于更广泛的传递信息,并不代表本网站赞同其观点,文章内容仅供参考。

本站是一个个人学习和交流平台,网站上部分文章为网站管理员和网友从相关媒体转载而来,并不用于任何商业目的,内容为作者个人观点, 并不代表本网站赞同其观点和对其真实性负责。

我们已经尽可能的对作者和来源进行了通告,但是可能由于能力有限或疏忽,导致作者和来源有误,亦可能您并不期望您的作品在我们的网站上发布。我们为这些问题向您致歉,如果您在我站上发现此类问题,请及时联系我们,我们将根据您的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。