1.JVM常用的参数有哪些? 标准参数

-versIOn -help -server -cp

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(1)

3.1.2 -X参数

非标准参数,也就是在JDK各个版本中可能会变动

-Xint 解释执行 -Xcomp 第一次使用就编译成本地代码 -Xmixed 混合模式,JVM自己来决定

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(2)

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面试题(面试还不懂如何回答面试JVM相关的问题)(3)

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(4)

**值得注意的是"="表示默认值,":="表示被用户或JVM修改后的值****要想查看某个进程具体参数的值,可以使用jinfo,这块后面聊****一般要设置参数,可以先查看一下当前参数是什么,然后进行修改**

由于文章的篇幅有限,老师这里只能为大家展示部分内容,同时JVM相关的问题全部给大家整理成了PDF,需要领取的直接点赞、转发文章之后,私信我【666】获取即可!

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

设置标记周期完成后,存活数据上限为 G1MixedGCLIveThresholdPercent 的旧区域执行混合垃圾回收的目标次数

默认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.

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(5)

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

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(6)

(3)修改

参数只有被标记为manageable的flags可以被实时修改

jinfo -flag [ |-] PID jinfo -flag <name>=<value> PID

(4)查看曾经赋过值的一些参数

jinfo -flags PID

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(7)

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次

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(8)

(3)查看垃圾收集信息

jstat -gc PID 1000 10

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(9)

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

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(10)

(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锁"); } } } } } }

  • 运行结果

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(11)

  • jstack分析

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(12)

把打印信息拉到最后可以发现

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(13)

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

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(14)

(3)dump出堆内存相关信息

jmap -dump:format=b,file=heap.hprof PID

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(15)

(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秒

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(16)

4.1.2 内存溢出(OOM)

一般会有两个原因:

(1)大并发情况下

(2)内存泄露导致内存溢出

4.1.2.1 大并发[秒杀]

浏览器缓存、本地缓存、验证码

CDN静态资源服务器

集群 负载均衡

动静态资源分离、限流[基于令牌桶、漏桶算法]

应用级别缓存、接口防刷限流、队列、Tomcat性能优化

异步消息中间件

Redis热点数据对象缓存

分布式锁、数据库锁

5分钟之内没有支付,取消订单、恢复库存等

4.1.2.2 内存泄露导致内存溢出

ThreadLocal引起的内存泄露,最终导致内存溢出

public class TLController { @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调优最佳实战

官网:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations)

(1)不要手动设置新生代和老年代的大小,只要设置整个堆的大小

why:https://blogs.oracle.com/poonam/increased-heap-usage-with-g1-gc

G1收集器在运行过程中,会自己调整新生代和老年代的大小 其实是通过adapt代的大小来调整对象晋升的速度和年龄,从而达到为收集器设置的暂停时间目标 如果手动设置了大小就意味着放弃了G1的自动调优

(2)不断调优暂停时间目标

一般情况下这个值设置到100ms或者200ms都是可以的(不同情况下会不一样),但如果设置成50ms就不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成Full GC。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到满足。

(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性能优化指南

最全的jvm面试题(面试还不懂如何回答面试JVM相关的问题)(17)

垃圾回收的预调优

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

,