写点什么

【死磕 JVM】什么是 JVM 调优?

用户头像
牧小农
关注
发布于: 2021 年 04 月 26 日
【死磕JVM】什么是JVM调优?

想要学习 JVM 调优,我们必须提前知道他们的一些参数,这样才方便我们更好的去使用他们

JVM 常用命令行参数

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

1. 查看参数列表

虚拟机参数分为基本和扩展两类,在命令行中输入 JAVA_HOME\bin\java就可得到基本参数列表。在命令行输入 JAVA_HOME\bin\java –X就可得到扩展参数列表。

2. 基本参数说明:

-client,-server: 两种 Java 虚拟机启动方式,client 模式启动比较快,但是性能和内存管理相对较差,server 模式启动比较慢,但是运行性能比较高,windos 上采用的是 client 模式,Linux 采用 server 模式


-classpath -cp: 虚拟机在运行一个类的时候,需要将其转入内存中,虚拟机搜索类的方式和顺序:Bootstrap classes、Extension classes、User classes。Bootstrap 中的路径是虚拟机自带的 jar 或 zip 文件,虚拟机首先搜索这些包文件,用 System.getProperty("sun.boot.class.path")可得到虚拟机搜索的包名。Extension 是位于 jre\lib\ext 目录下的 jar 文件,虚拟机在搜索完 Bootstrap 后就搜索该目录下的 jar 文件。用 System. getProperty("java.ext.dirs”)可得到虚拟机使用 Extension 搜索路径。User classes 搜索顺序为当前目录、环境变量 CLASSPATH、-classpath。


-classpath: 告诉虚拟机搜索目录名、jar 文档名、zip 文档名


-verbose[:class|gc|jni]: 在输出设备上显示虚拟机运行信息-verbose:class 输出虚拟机装入的类的信息-verbose:gc 在虚拟机发生内存回收时输出设备信息,用来监视虚拟机内存回收的情况-verbose:jni 虚拟机调用 native 方法时输出设置显示信息,用来监视虚拟机调用本地方法的情况


-version:显示可运行的虚拟机版本信息


-showversion:显示版本信息以及帮助信息

3. 扩展参数说明:

-Xmixed: 设置 -client 模式虚拟机对使用频率高的方式进行 Just-In-Time 编译和执行,对其他方法使用解释方式执行,该方式是虚拟机缺省模式


-Xint: 设置-client 模式下运行的虚拟机以解释方式执行类的字节码,不将字节码编译为本机码,有可能会损失性能


-Xbootclasspath:path、-Xbootclasspath/a:path、-Xbootclasspath/p:path: 改变虚拟机装载系统运行包 rt.jar,而从-Xbootclasspath 中设定的搜索路径中装载系统运行类。除非你自己能写一个运行时,否则不会用到该参数。/a:将在缺省搜索路径后加上 path 中的搜索路径。/p:在缺省搜索路径前先搜索 path 中的搜索路径。


-Xnoclassgc: 关闭虚拟机对 class 的垃圾回收功能,有可能会导致 OutOfMemoryError


-Xincgc: 启动增量垃圾收集器,缺省是关闭的,增量垃圾收集器能减少偶然发生的长时间的垃圾回收造成的暂停时间,但增量垃圾收集器和应用程序并发执行,会占用部分 CPU 在应用程序上的功能


-Xloggc:file: 将虚拟机每次垃圾回收的信息写到日志文件中,文件名由 file 指定,文件格式是平文件,内容和-verbose:gc 输出内容相同


-Xms20M: 设置虚拟机可用内存堆的初始大小为 20M,大小为 1024 的整数倍并且要大于 1MB,可用 K 或者 M 为单位来设置较大内存数,初始堆大小为 2MB,例如:-Xms256M


-Xmx20M: 设置虚拟机内存堆的最大可用大小,该值必须为 1024 的整数倍,并且要大于 2MB,可用 K 或者 M 为单位来设置较大的内存数,例如:-Xmx81920K,-Xmx80M,当应用程序申请了大内存运行时虚拟机抛出 java.lang.OutOfMemoryError,就需要使用-Xmx 来设置了


-Xss128K: 设置线程栈的大小,和-Xmx 类似,可以用 K 或 M 来设置较大的值,也可以在 Java 中创建线程对象时设置栈的大小


-Xoss128k: 设置本地方法栈的大小为 128K,不过 HotSpot 不区分虚拟机栈和本地方法栈,所以这个参数对于 HotSpot 是无效的


-XX:PermSize=10M: 表示 JVM 初始分配的永久代的容量,必须以 M 为单位


-XX:MaxPermSize=10M: 表示 JVM 允许分配的永久代的最大容量,必须以 M 为单位,大部分情况下这个参数默认为 64M


-XX:NewRatio=4: 表示设置年轻代:老年代的大小比值为 1:4,这意味着年轻代占整个堆的 1/5


-XX:SurvivorRatio=8: 表示设置 2 个 Survivor 区:1 个 Eden 区的大小比值为 2:8,这意味着 Survivor 区占整个年轻代的 1/5,这个参数默认为 8


-Xmn20M: 表示设置年轻代的大小为 20M


-XX:+HeapDumpOnOutOfMemoryError: 表示可以让虚拟机在出现内存溢出异常时 Dump 出当前的堆内存转储快照


-XX:+UseG1GC: 表示让 JVM 使用 G1 垃圾收集器


-XX:+PrintGCDetails: 表示在控制台上打印出 GC 具体细节


-XX:+PrintGC: 表示在控制台上打印出 GC 信息


-XX:PretenureSizeThreshold=3145728 表示对象大于 3145728(3M)时直接进入老年代分配,这里只能以字节作为单位


-XX:MaxTenuringThreshold=1: 表示对象年龄大于 1,自动进入老年代

虚拟机参数分类

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


例如:java -version、java -X


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 memoryjava -XX:+PrintCommandLineFlags HelloGCjava -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGCPrintGCDetails PrintGCTimeStamps PrintGCCausesjava -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGCjava -XX:+PrintFlagsInitial 默认参数值 java -XX:+PrintFlagsFinal 最终参数值 java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数 java -XX:+PrintFlagsFinal -version |grep GC

调优前的基础概念

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


所谓的调优,首先自己要明确,想要的是什么,是吞吐量还是响应时间,还是在满足一定的响应时间的情况下,要求达到多大的吞吐量,一般来说根据业务类型去选择对应的调优方式,比如网站需要的是响应时间优先,JDK1.8 尽量选 G1,那如果是数据挖掘的需要使用的是吞吐量。

什么是调优

在没有接触过调优之前我们理解的 JVM 调优就是解决 OOM 问题,OOM 只是 JVM 调优的一部分


  • 一般是根据需求进行 JVM 规划和预调优

  • 优化运行 JVM 运行环境(慢,卡顿)

  • 解决 JVM 运行过程中出现的各种问题(OOM)


首先的话,调优是从业务场景开始的,如果没有业务场景的 JVM 调优都是不靠谱的,比如有时间在实际项目中,有很多个类,成千上万个代码,你怎么知道具体是哪个代码有问题,就算我们知道有段代码频繁的 full gc,但是可能过一段时间就 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

  • 或者每天产生一个日志文件


在生产环境中日志文件,后面日志名字,按照系统时间产生,循环产生,日志个数五个,每个大小 20M,这样的好处在于整体大小 100M,能 控制整体文件大小


  • 观察日志情况

调优案例

案例一

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


这个问题比较鸡肋,因为很多不同的服务器配置都能够支撑


比如有一百万个订单,每个小时不会产生很高的并发量,我们寻找高峰时间,做一个假设 100W 订单有 72W 订单在高峰期产生,比如一个小时平均 36W 订单,所以我们内存选择大小是按照巅峰时间选择的,很多时间我们可能就是去做压测,实在不行就添加 CPU 和内存

案例二

12306 遭遇春节大规模抢票应该如何支撑订单信息每天固定,可以丢到缓存中,不同的业务逻辑有不同的业务设计,12306 应该是中国并发量最大的秒杀网站,号称并发 100W,就是说每秒进行百万次的业务逻辑的处理,估计淘宝一年最高的是 54W 并发。


如果解决这个问题呢,看下面所示:


CDN -》 LVS -》 NGINX -》 业务系统 -》每台机器 1W 并发


普通电商的下单流程一般是:


订单 -》下单-》 订单系统减库存 -》 等待用户付款这个事务如果同步的方式完成,TPS 是支撑不了多长时间的


但是在 12306 里面的模型是下单-》 减库存和订单同时异步进行 -》 等待付款异步是当你下完订单之后,它一个线程去减库存,另外一个线程直接把你下单的信息扔到 kafka 或者 redis 里面直接返回 OK,你下单成功后等待你付款,什么时候你付款完成后面那些个订单处理线程就会去里面拿数据,这个处理完了就会持久化到 Hbase 或者是 mysql,一般大流量的处理方法核心思想就是:分而治之

JVM 优化

比如我有一个 50 万 PV 的资料类网站(从磁盘提取文档到内存)原服务器 32 位,1.5G 的堆,用户反馈网站比较缓慢,如果对它进行升级,新服务器 64 位,16G 的堆内存,用户还是反馈卡顿,而且还比之前更严重,这个是因为什么呢?一般来说很多用户去浏览数据,很多数据会 load 到内存中,导致内存不足,频繁的 GC,STW 时间过长,响应时间就会变慢,那我们应该怎么办呢,使用 PS-> PN+CMS 或者 G1。


还有一个就是系统 CPU 经常 100%,我们要如何进行调优呢?


首先我们可以想到 CPU100%那么一定有线程在占用系统资源


  1. 找出哪个进程 CPU 高 (top 命令)

  2. 该进程中的哪个线程 CPU 高(top -Hp)

  3. 导出该线程的堆栈

  4. 查找哪个方法(栈挣)比较消耗时间

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

总结

今天我们只是讲解了一些基本的操作,具体怎么操作该怎么办呢?这一部分小农会在下一部分中进行讲解,今天主要带大家了解一些常用的参数,告诉大家怎么去使用和一些前置知识,下面我会对这些问题做一个实战性的讲解,感兴趣的小伙伴记得来个一键三连,感谢大家。


我是牧小农,怕什么真理无穷,进一步有进一步的欢喜,大家加油!!!

发布于: 2021 年 04 月 26 日阅读数: 35
用户头像

牧小农

关注

业精于勤,荒于嬉;行成于思,毁于随! 2019.02.13 加入

业精于勤荒于嬉,行成于思毁于随

评论

发布
暂无评论
【死磕JVM】什么是JVM调优?