1.JVM常用的参数有哪些?
标准参数
-version -help -server -cp
3.1.2 -X参数
非标准参数,也就是在JDK各个版本中可能会变动
-Xint 解释执行 -Xcomp 第一次使用就编译成本地代码 -Xmixed 混合模式,JVM自己来决定
3.1.3 -XX参数
使用得最多的是参数类型
非标准化参数,相对不稳定,主要用于JVM调优和Debug
a.Boolean类型 格式:-XX:[+-]<name> +或-表示启用或者禁用name属性 比如:-XX:+UseConcMarkSweepGC 表示启用CMS类型的垃圾回收器 -XX:+UseG1GC 表示启用G1类型的垃圾回收器 b.非Boolean类型 格式:-XX<name>=<value>表示name属性的值是value 比如:-XX:MaxGCPauseMillis=500
3.1.4 其他参数
-Xms1000M等价于-XX:InitialHeapSize=1000M -Xmx1000M等价于-XX:MaxHeapSize=1000M -Xss100等价于-XX:ThreadStackSize=100
所以这块也相当于是-XX类型的参数
3.1.5 查看参数
java -XX:+PrintFlagsFinal -version > flags.txt
**值得注意的是"="表示默认值,":="表示被用户或JVM修改后的值**
**要想查看某个进程具体参数的值,可以使用jinfo,这块后面聊**
**一般要设置参数,可以先查看一下当前参数是什么,然后进行修改**
由于文章的篇幅有限,老师这里只能为大家展示部分内容,同时JVM相关的问题全部给大家整理成了PDF,需要领取的可以点击此处来获取就可以了!
3.1.6 设置参数的常见方式
- 开发工具中设置比如IDEA,eclipse
- 运行jar包的时候:java -XX:+UseG1GC xxx.jar
- web容器比如tomcat,可以在脚本中进行设置
- 通过jinfo实时调整某个java进程的参数(参数只有被标记为manageable的flags可以被实时修改)
3.1.7 实践和单位换算
1Byte(字节)=8bit(位) 1KB=1024Byte(字节) 1MB=1024KB 1GB=1024MB 1TB=1024GB
(1)设置堆内存大小和参数打印 -Xmx100M -Xms100M -XX:+PrintFlagsFinal (2)查询+PrintFlagsFinal的值 :=true (3)查询堆内存大小MaxHeapSize := 104857600 (4)换算 104857600(Byte)/1024=102400(KB) 102400(KB)/1024=100(MB) (5)结论 104857600是字节单位
3.1.8 常用参数含义
参数 |
含义 |
说明 |
-XX:CICompilerCount=3 |
最大并行编译数 |
如果设置大于1,虽然编译速度会提高,但是同样影响系统稳定性,会增加JVM崩溃的可能 |
-XX:InitialHeapSize=100M |
初始化堆大小 |
简写-Xms100M |
-XX:MaxHeapSize=100M |
最大堆大小 |
简写-Xms100M |
-XX:NewSize=20M |
设置年轻化的大小 |
|
-XX:MaxNewSize=50M |
年轻代最大大小 |
|
-XX:OldSize=50M |
设置老年代大小 |
|
-XX:MetaspaceSize=50M |
设置方法区大小 |
|
-XX:MaxMetaspaceSize=50M |
方法区最大大小 |
|
-XX:+UseParallelGC |
使用UseParallelGC |
新生代,吞吐量优先 |
-XX:+UseParallelOldGC |
使用UseParallelOldGC |
老年代,吞吐量优先 |
-XX:+UseConcMarkSweepGC |
使用CMS |
老年代,停顿时间优先 |
-XX:+UseG1GC |
使用G1GC |
新生代,老年代,停顿时间优先 |
-XX:NewRatio |
新老生代的比值 |
**比如-XX:Ratio=4,则表示新生代:老年代=1:4,也就是新生代占整个堆内存的1/5** |
-XX:SurvivorRatio |
两个S区和Eden区的比值 |
**比如-XX:SurvivorRatio=8,也就是(S0+S1):Eden=2:8,也就是一个S占整个新生代的1/10** |
-XX:+HeapDumpOnOutOfMemoryError |
启动堆内存溢出打印 |
当JVM堆内存发生溢出时,也就是OOM,自动生成dump文件 |
-XX:HeapDumpPath=heap.hprof |
指定堆内存溢出打印目录 |
表示在当前目录生成一个heap.hprof文件 |
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:g1-gc.log |
打印出GC日志 |
可以使用不同的垃圾收集器,对比查看GC情况 |
-Xss128k |
设置每个线程的堆栈大小 |
经验值是3000-5000最佳 |
-XX:MaxTenuringThreshold=6 |
提升年老代的最大临界值 |
默认值为 15 |
-XX:InitiatingHeapOccupancyPercent |
启动并发GC周期时堆内存使用占比 |
G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比. 值为 0 则表示”一直执行GC循环”. 默认值为 45. |
-XX:G1HeapWastePercent |
允许浪费对空间的占比 |
默认是10%,如果并发标记可回收的空间小于10%,则不会触发MixedGC。 |
-XX:MaxGCPauseMillis=200ms |
G1最大停顿时间 |
暂停时间不能太小,太小的话就会导致出现G1跟不上垃圾产生的速度。最终退化成Full GC。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。 |
-XX:ConcGCThreads=n |
并发垃圾收集器使用的线程数量 |
默认值随JVM运行的平台不同而不同 |
-XX:G1MixedGCLiveThresholdPercent=65 |
混合垃圾回收周期中要包括的旧区域设置占用率阈值 |
默认占用率为 65% |
-XX:G1MixedGCCountTarget=8 |
设置标记周期完成后,存活数据上限为 |
默认8次混合垃圾回收,混合回收的目标是要控制在此目标次数以内 |
-XX:G1OldCSetRegionThresholdPercent=1 |
描述Mixed GC时,Old Region被加入到CSet中 |
默认情况下,G1只把10%的Old Region加入到CSet中 |
2.JVM中常用的命令有哪些?
2.1.1 jps
查看java进程
The jps command lists the instrumented Java HotSpot VMs on the target system. The command is limited to reporting information on JVMs for which it has the access permissions.
2.1.2 jinfo
(1)实时查看和调整JVM配置参数
The jinfo command prints Java configuration information for a specified Java process or core file or a remote debug server. The configuration information includes Java system properties and Java Virtual Machine (JVM) command-line flags.
(2)查看用法
jinfo -flag name PID 查看某个java进程的name属性的值
jinfo -flag MaxHeapSize PID jinfo -flag UseG1GC PID
(3)修改
参数只有被标记为manageable的flags可以被实时修改
jinfo -flag [+|-] PID jinfo -flag <name>=<value> PID
(4)查看曾经赋过值的一些参数
jinfo -flags PID
2.1.3 jstat
(1)查看虚拟机性能统计信息
The jstat command displays performance statistics for an instrumented Java HotSpot VM. The target JVM is identified by its virtual machine identifier, or vmid option.
(2)查看类装载信息
jstat -class PID 1000 10 查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10次
(3)查看垃圾收集信息
jstat -gc PID 1000 10
2.1.4 jstack
(1)查看线程堆栈信息
The jstack command prints Java stack traces of Java threads for a specified Java process, core file, or remote debug server.
(2)用法
jstack PID
(4)排查死锁案例
- DeadLockDemo
//运行主类 public class DeadLockDemo { public static void main(String[] args) { DeadLock d1=new DeadLock(true); DeadLock d2=new DeadLock(false); Thread t1=new Thread(d1); Thread t2=new Thread(d2); t1.start(); t2.start(); } } //定义锁对象 class MyLock{ public static Object obj1=new Object(); public static Object obj2=new Object(); } //死锁代码 class DeadLock implements Runnable{ private boolean flag; DeadLock(boolean flag){ this.flag=flag; } public void run() { if(flag) { while(true) { synchronized(MyLock.obj1) { System.out.println(Thread.currentThread().getName()+"----if获得obj1锁"); synchronized(MyLock.obj2) { System.out.println(Thread.currentThread().getName()+"----if获得obj2锁"); } } } } else { while(true){ synchronized(MyLock.obj2) { System.out.println(Thread.currentThread().getName()+"----否则获得obj2锁"); synchronized(MyLock.obj1) { System.out.println(Thread.currentThread().getName()+"----否则获得obj1锁"); } } } } } }
- 运行结果
- jstack分析
把打印信息拉到最后可以发现
2.1.5 jmap
(1)生成堆转储快照
The jmap command prints shared object memory maps or heap memory details of a specified process, core file, or remote debug server.
(2)打印出堆内存相关信息
jmap -heap PID
jinfo -flag UsePSAdaptiveSurvivorSizePolicy 35352 -XX:SurvivorRatio=8
(3)dump出堆内存相关信息
jmap -dump:format=b,file=heap.hprof PID
(4)要是在发生堆内存溢出的时候,能自动dump出该文件就好了
一般在开发中,JVM参数可以加上下面两句,这样内存溢出时,会自动dump出该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
设置堆内存大小: -Xms20M -Xmx20M 启动,然后访问localhost:9090/heap,使得堆内存溢出
面试官问你项目中的JVM性能优化你怎么聊?
性能优化
JVM的性能优化可以分为代码层面和非代码层面。
在代码层面,大家可以结合字节码指令进行优化,比如一个循环语句,可以将循环不相关的代码提取到循环体之外,这样在字节码层面就不需要重复执行这些代码了。
在非代码层面,一般情况可以从内存、gc以及cpu占用率等方面进行优化。
注意,JVM调优是一个漫长和复杂的过程,而在很多情况下,JVM是不需要优化的,因为JVM本身已经做了很多的内部优化操作。
那今天我们就从内存、gc以及cpu这3个方面和大家一起探讨一下JVM的优化,但是大家要注意的是****不要为了调优和调优。
4.1 内存
4.1.1 内存分配
正常情况下不需要设置,那如果是促销或者秒杀的场景呢?
每台机器配置2c4G,以每秒3000笔订单为例,整个过程持续60秒
4.1.2 内存溢出(OOM)
一般会有两个原因:
(1)大并发情况下
(2)内存泄露导致内存溢出
4.1.2.1 大并发[秒杀]
浏览器缓存、本地缓存、验证码
CDN静态资源服务器
集群+负载均衡
动静态资源分离、限流[基于令牌桶、漏桶算法]
应用级别缓存、接口防刷限流、队列、Tomcat性能优化
异步消息中间件
Redis热点数据对象缓存
分布式锁、数据库锁
5分钟之内没有支付,取消订单、恢复库存等
4.1.2.2 内存泄露导致内存溢出
ThreadLocal引起的内存泄露,最终导致内存溢出
publicclassTLController { @RequestMapping(value = "/tl")public String tl(HttpServletRequest request) { ThreadLocal<Byte[]> tl = new ThreadLocal<Byte[]>(); // 1MB tl.set(new Byte[1024*1024]); return"ok"; } }
(1)上传到阿里云服务器
jvm-case-0.0.1-SNAPSHOT.jar
(2)启动
java -jar -Xms1000M -Xmx1000M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=jvm.hprof jvm-case-0.0.1-SNAPSHOT.jar
(3)使用jmeter模拟10000次并发
39.100.39.63:8080/tl
(4)top命令查看
top top -Hp PID
(5)jstack查看线程情况,发现没有死锁或者IO阻塞的情况
jstack PID java -jar arthas.jar ---> thread
(6)查看堆内存的使用,发现堆内存的使用率已经高达88.95%
jmap -heap PID java -jar arthas.jar ---> dashboard
(7)此时可以大体判断出来,发生了内存泄露从而导致的内存溢出,那怎么排查呢?
jmap -histo:live PID | more 获取到jvm.hprof文件,上传到指定的工具分析,比如heaphero.io
4.2 GC
这里以G1垃圾收集器调优为例
4.2.1 是否选用G1
官网:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/G1.html#use_cases
(1)50%以上的堆被存活对象占用 (2)对象分配和晋升的速度变化非常大 (3)垃圾回收时间比较长
4.2.2 G1调优
(1)使用G1GC垃圾收集器: -XX:+UseG1GC
修改配置参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间
Throughput Min Pause Max Pause Avg Pause GC count 99.16% 0.00016s 0.0137s 0.00559s 12
(2)调整内存大小再获取gc日志分析
-XX:MetaspaceSize=100M -Xms300M -Xmx300M
比如设置堆内存的大小,获取到gc日志,使用GCViewer分析吞吐量和响应时间
Throughput Min Pause Max Pause Avg Pause GC count 98.89% 0.00021s 0.01531s 0.00538s 12
(3)调整最大停顿时间
-XX:MaxGCPauseMillis=200 设置最大GC停顿时间指标
比如设置最大停顿时间,获取到gc日志,使用GCViewer分析吞吐量和响应时间
Throughput Min Pause Max Pause Avg Pause GC count 98.96% 0.00015s 0.01737s 0.00574s 12
(4)启动并发GC时堆内存占用百分比
-XX:InitiatingHeapOccupancyPercent=45 G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比例。值为 0 则表示“一直执行GC循环)'. 默认值为 45 (例如, 全部的 45% 或者使用了45%).
比如设置该百分比参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间
Throughput Min Pause Max Pause Avg Pause GC count 98.11% 0.00406s 0.00532s 0.00469s 12
4.2.3 G1调优最佳实战
(1)不要手动设置新生代和老年代的大小,只要设置整个堆的大小
why:https://blogs.oracle.com/poonam/increased-heap-usage-with-g1-gc
G1收集器在运行过程中,会自己调整新生代和老年代的大小 其实是通过adapt代的大小来调整对象晋升的速度和年龄,从而达到为收集器设置的暂停时间目标 如果手动设置了大小就意味着放弃了G1的自动调优
(2)不断调优暂停时间目标
一般情况下这个值设置到100ms或者200ms都是可以的(不同情况下会不一样),但如果设置成50ms就不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成FullGC。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到满足。
(3)使用-XX:ConcGCThreads=n来增加标记线程的数量
IHOP如果阀值设置过高,可能会遇到转移失败的风险,比如对象进行转移时空间不足。如果阀值设置过低,就会使标记周期运行过于频繁,并且有可能混合收集期回收不到空间。 IHOP值如果设置合理,但是在并发周期时间过长时,可以尝试增加并发线程数,调高ConcGCThreads。
**(4)MixedGC调优 **
-XX:InitiatingHeapOccupancyPercent-XX:G1MixedGCLiveThresholdPercent-XX:G1MixedGCCountTarger-XX:G1OldCSetRegionThresholdPercent
(5)适当增加堆内存大小
(6)不正常的Full GC
有时候会发现系统刚刚启动的时候,就会发生一次Full GC,但是老年代空间比较充足,一般是由Metaspace区域引起的。可以通过MetaspaceSize适当增加其大家,比如256M。
4.3 JVM性能优化指南
垃圾回收的预调优
1.上线前内存的配置是否符合并发要求,不符合需要调整
从对象的内存布局开始计算,计算出堆内存大小,然后*并发时间,算出堆内存占用情况,然后计算内存是否扛得住,考虑负载均衡以及冗余情况
2.压测的吞吐是多少 ,一般控制在95%以上
极限吞吐的情况下,不超过1%的偏差,调整停顿时间,使用GC view或者其他GC工具分析
Throughput Min Pause Max Pause Avg Pause GC count 99.16% 0.00016s 0.0137s 0.00559s 12
3.Full GC的频率以及Young GC的频率,是否需要更换GC收集器
比如前面讲的G1
4.可靠性观测:会不会出现内存泄露,这个可以观测出来的,根据内存变化情况
可以进行两次GC,然后对于Dump出的文件,分析文件内存移动回收情况
5.垃圾收集器的参数是否要改变,这个也是根据吞吐量以及GC来确定的
这个跟第2个情况配合使用
6.CPU的使用率情况
分析死锁以及内存泄漏相关情况
死锁:jstack