面试还不懂如何回答面试JVM相关的问题,看这一篇就够了

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 1.JVM常用的参数有哪些?标准参数-version-help-server-cp3.1.2 -X参数非标准参数,也就是在JDK各个版本中可能会变动

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

设置标记周期完成后,存活数据上限为
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.

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调优最佳实战

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

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

whyhttps://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

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
20天前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
2月前
|
安全 Java 应用服务中间件
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
什么是类加载器,类加载器有哪些;什么是双亲委派模型,JVM为什么采用双亲委派机制,打破双亲委派机制;类装载的执行过程
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
|
22天前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
23 4
|
25天前
|
Java API 对象存储
JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
本文详细解析了JVM类加载过程的关键步骤,包括加载验证、准备、解析和初始化等阶段,并介绍了元数据区、程序计数器、虚拟机栈、堆内存及本地方法栈的作用。通过本文,读者可以深入了解JVM的工作原理,理解类加载器的类型及其机制,并掌握类加载过程中各阶段的具体操作。
|
25天前
|
存储 缓存 JavaScript
JVM面试真题总结(一)
JVM面试真题总结(一)
|
2月前
|
存储 缓存 监控
【Java面试题汇总】JVM篇(2023版)
JVM内存模型、双亲委派模型、类加载机制、内存溢出、垃圾回收机制、内存泄漏、垃圾回收流程、垃圾回收器、G1、CMS、JVM调优
【Java面试题汇总】JVM篇(2023版)
|
20天前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
|
3月前
|
存储 安全 Java
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别;什么是程序计数器,堆,虚拟机栈,栈内存溢出,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
|
4月前
|
Java 编译器 程序员
JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别
JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别
JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别
|
3月前
|
缓存 算法 Java
这些年背过的面试题——JVM篇
本文是技术人面试系列JVM篇,面试中关于JVM都需要了解哪些基础?一文带你详细了解,欢迎收藏!