文章目录

  • 1. JVM的参数类型
  • 2. 你平常工作时用过的JVM常用基本配置参数有哪些?
  • 3. 谈一谈强引用、软引用、弱引用、虚引用分别是什么?
  • 3.1 强引用(默认支持模式)
  • 3.2 软引用
  • 3.3 弱引用
  • 3.4 虚引用
  • 3.5 GCRoots和四大引用总结
  • 4. 谈谈你对OOM的认识
  • 6. GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈
  • 6.1 四种主要垃圾收集器
  • 7. 怎么查看服务器默认的垃圾回收器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?
  • 7.1 怎么查看服务器默认的垃圾回收器是那个?
  • 7.2 默认的垃圾回收器有哪几种?
  • 7.3 垃圾收集器
  • 7.3.1 串行垃圾收集器(Serial)
  • 7.3.2 并行收集器(ParNew)
  • 7.3.3 并行收集器(Parallel Scavenge)
  • 7.3.4 Parallel Old
  • 7.3.5 并发标记清除GC(CMS)(重要)
  • 7.3.5 G1垃圾回收器
  • 7.3.6 如何选择垃圾收集器
  • 7.3.7 JVM GC结合SpringBoot微服务优化
  • 7.4 生产环境服务器变慢,诊断思路和性能评估?
  • 7.4.1 整机
  • 7.4.2 CPU
  • 7.4.3 内存
  • 7.4.4 硬盘
  • 7.4.5 磁盘IO
  • 7.4.6 网络IO
  • 7.5 假如生产环境出现CPU占用过高,请谈谈你的分析思路和定位


1. JVM的参数类型

  1. Boolean类型
    公式:
    -XX:+或者-某个属性值
    其中+表示开启,-表示关闭
  2. KV设值类型
    公式:
    -XX:属性key=属性值value
    case:
    -XX:MetaSpaceSize=128m
    -XX:MaxTenuringThreshold=15
  3. jinfo举例,如何查看当前运行程序的配置
    第一种,查看参数盘点家底
    公式:
    jps 查看进程编号
    jinfo -flag 配置项 进程编号
    jinfo -flags 进程编号 ----查看该进程所有参数
    第二种,查看参数盘点家底
    查看初始化: java -XX:+PrintFlagsInitial
    查看修改更新:java -XX:+PrintFlagsFinal -version 其中=表示未修改过,:=表示修改过
  4. KVM笔试题 kvm常见面试题_职场和发展

  5. 打印命令行参数:java -XX:+PrintCommandLineFlags
  6. KVM笔试题 kvm常见面试题_老年代_02

2. 你平常工作时用过的JVM常用基本配置参数有哪些?

  1. -Xms:初始大小内存,默认为物理内存的1/64,等价于-XX:InitialHeapSize
  2. -Xmx: 最大分配内存,默认为物理内存1/4,等价于-XX:MaxHeapSize
  3. -Xss:设置单个线程栈的大小,一般默认为512k~1024k,等价于-XX:ThreadStackSize
  4. -Xmn:设置年轻代大小(一般不用调整)
  5. -XX:MetaspaceSize:设置元空间的大小;元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
    -Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=1024m -XX:+PrintFlagsFinal
  6. -XX:+PrintGCDetails:输出详细GC收集日志信息
  7. -XX:SurvivorRatio:设置新生代中eden和s0/s1空间的比例,默认-XX:SurviorRatio=8,Eden:s0:s1=8:1:1 ;假如-XX:SurvivorRatio=4,Eden:s0:s1=4:1:1,SurvivorRatio值就是设置eden区的比例占多少,s0/s1相同
  8. -XX:NewRatio: 配置年轻代与老年代在堆结构的占比,默认-XX:NewRatio=2新生代占1,老年代占2,年轻代占整个堆的1/3,假如-XX:NewRatio=4表示新生代占1,老年代占4,年轻代占整个堆的1/5,newRatio值就是设置老年代的占比,剩下的1给新生代(在s0/s1中交换15次后还存活的对象会存入老年代)。
  9. -XX:MaxTenringThrehold:设置垃圾最大年龄,如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存货时间,增加在年轻代即被回收的概率。(该值默认为15,且最大为15)

3. 谈一谈强引用、软引用、弱引用、虚引用分别是什么?

3.1 强引用(默认支持模式)

当内存不足,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,死都不收。

强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后和远都不会被用到VM也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。

对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null一般认为就是可以被垃圾收集的了(当然具体回收时机还是要看垃圾收集策略)。

public class strongReferenceDemo{
	public static void main(string[] args){
		object obj1 = new object();//这样定义的默认就是强引用
		object obj2 = obj1; //obj2引用赋值
		obj1 = null;//置空
		system.gc();
		system.out.println(obj2);//obj2依旧可以打印出来
	}
}

3.2 软引用

软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾收集。对于只有软引用的对象来说, 当系统内存充足时它不会被回收,当系统内存不足时它会被回收。 软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收!

3.3 弱引用

弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。

使用场景
假如有一个应用需要读取大量的本地图片:

  • 如果每次读取图片都从硬盘读取则会严重影响性能,
  • 如果一次性全部加载到内存中又可能造成内存溢出。

此时使用软引用可以解决这个问题。

设计思路是:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。

Map<String, SoftReference>imageCache = new HashMap<String, SoftReference>();

WeekHashMap:
WeekHashMap的key是弱引用,value是强引用。因此在一次gc后,key就会被回收,而value不会被回收,在这里可能会导致内存泄露。

3.4 虚引用

虚引用需要java.lang.ref.PhantomReference类来实现。

顿名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问付象,虚引用必须和引用队列(ReferenceQueue)联合使用。

引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。

PhantomReference的get方法总是返回nul因此无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段,可以被c回收,用来实现比finalization机制更灵活的回收操作。

换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java 技术允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

Java提供了4种引用类型,在垃圾回收的时候,都有自己各自的特点。
ReferenceQueue是用来配合引用工作的,没有ReferenceQueue一样可以运行。

创建引用的时候可以指定关联的队列,当GC释放对象内存的时候,会将引用加入到引用队列,如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,这相当于是一种通知机制。

当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种方式,JVN允许我们在对象被销毁后,做一些我们自己想做的事情。

3.5 GCRoots和四大引用总结

KVM笔试题 kvm常见面试题_KVM笔试题_03

4. 谈谈你对OOM的认识

  1. java.lang.StackOverflowError:栈溢出错误,比如没有停止条件的递归。
  2. java.lang.OutOfMemoryError:Java heap space 堆内存溢出
  3. java.lang.OutOfMemoryError:GC overhead limit exceeded
    GC回收时间过长时会抛出OutOfNemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存连续多次GC 都只回收了不到2%的极端情况下才会抛出。假如不抛出 GC overhead limit 错误会发生什么情况呢?那就是GC清理的这么点内存很快会再次填满,迫使Gc再次执行.这样就形成恶性循环,CPU使用率一直是100%,而GC却没有任何成果。
  4. java.lang.OutOfMemoryError:Direct buffer memory

写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,

它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避兔了在Java堆和Native堆中来回复制数据。

  • ByteBuffer.allocate(capability)第一种方式是分配VN堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢
  • ByteBuffer.allocteDirect(capability))第2种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。

但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,那程序就直接崩溃了。

  1. java.lang.OutOfMemoryError:unable to create new native thread(重要)

高并发请求服务器时,经常出现如下异常:java.lang.OutOfMemoryError: unable to create new native thread,准确的讲该native thread异常与对应的平台有关

导致原因:
1. 你的应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限
2. 你的服务器并不允许你的应用程序创建这么多线程, Linux系统默认允许单个进程可以创建的线程数是1024个,你的应用创建超过这个数量,就会报java.Lang.OutOfMNemoryError: unable to create new native thread

解决办法:
1. 想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码将线程数降到最低
2. 对于有的应用,确实需要创建很多线程,远超过Linux系统的默认1024个线程的限制,可以通过修改Uinux服务器配置,扩大linux默认限制

  1. java.lang.OutOfMemoryError:MetaSpace

JVM参数
-XX : Metaspacesize=8m
-XX:MaxMetaspacesize=8m

Java 8及之后的版本使用Metaspace来替代永久代。

Metaspace是方法区在HotSpot中的实现,它与持久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存也即在java8中, class metadata(the virtual machines internal presentation of Java class),被存储在叫做 Metaspace 的native memory

永久代(java8后被原空间Metaspace取代了)存放了以下信息:

  • 虚拟机加载的类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码

模拟Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Metaspace指定的空间大小的。

6. GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈

GC算法(引用计数/复制/标记清除/标记整理)是内存回收的方法论,垃圾收集器是算法落地实现。

因为目前为止没有完美的收集器出现,更加没有万能的收集器,只是针对具体应用最合适的收集器,进行分代收集。

6.1 四种主要垃圾收集器

  1. 串行垃圾回收器(Serial):它为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务器环境。
  2. 并行垃圾回收器(Parallel):多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理首台处理等弱交互场景
  3. 并发垃圾回收器(CMS):用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程,互联网公司多用它,适用于对响应时间有要求的场景

KVM笔试题 kvm常见面试题_职场和发展_04

  1. G1垃圾回收器:G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收

7. 怎么查看服务器默认的垃圾回收器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?

7.1 怎么查看服务器默认的垃圾回收器是那个?

java -XX:PrintCommandLineFlags -version

KVM笔试题 kvm常见面试题_职场和发展_05

7.2 默认的垃圾回收器有哪几种?

java的GC回收的类型主要有几种:
UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC

7.3 垃圾收集器

KVM笔试题 kvm常见面试题_垃圾收集器_06

部分参数说明:

  • DefNew:Default New Generation
  • Tenured:Old
  • ParNew:Parallel New Generation
  • PSYoungGen:Parallel Scavenge
  • ParOldGen:Parallel Old Generation

7.3.1 串行垃圾收集器(Serial)

串行收集器是最古老,最稳定以及效率高的收集器,只使用一个线程去回收。但其在进行垃圾收集过程中可能会产生较长的停顿(Stop-The-Word”状态)。虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销可以获得最高的单线程垃圾收集效率因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器

对应JVM参数是:-XX:+UseSerialGC

开启后会使用:Serial(Youngl区用)+Serial Old(Old区用)的收集器组合

表示:新生代、老年代都会使用串行回收收集器,新生代使用复制算法,老年代使用标记-整理算法

-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGc

7.3.2 并行收集器(ParNew)

一句话:使用多线程进行垃圾回收,在垃圾收集时,会Stop-the-World暂停其他所有的工作线程直到它收集结束。

ParNew收集器其实就是Seria收集器新生代的并行多线程版本,最常见的应用场景是配合老年代的CMS GC工作,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工.作线程。它是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。

常用对应JVM参数: -XX:+UseParNewGC启用ParNew收集器,只影响新生代的收集,不影响老年代

开启上述参数后,会使用:ParNew(Young区用)+ Serial Old的收集器组合,新生代使用复制算法,老年代采用标记-整理算法

但是,ParNew+Tenured这样的搭配,java8已经不再被推荐Java HotSpot™64-Bit Server VM warning:
Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
备注
-XX:ParallelGCThreads
限制线程数量,默认开启和CPU数目相同的线程数

-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParNewGC

7.3.3 并行收集器(Parallel Scavenge)

Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器,使用复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器。
一句话:串行收集器在新生代和老年代的并行化

它重点关注的是:
可控制的吞吐量(Thoughput=运行用户代码时间(运行用户代码时间+垃圾收集时间),也即比如程序运行100分钟,垃圾收集时间1分钟,吞吐量就是99% )。高吞吐量意味着高效利用CPU的时间,它多用于在后台运算而不需要太多交互的任务。

自适应调节策略也是ParallelScavenge收集器与ParNew收集器的一个重要区别。(自适应调节策略:虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间(-XX:MaxGCPauseMillis)或最大的吞吐量。

常用JVM参数:**-XX:+UseParallelGC或-XX:+UseParallelOIdGC(可互相激活)**使用Parallel Scanvenge收集器
开启该参数后:新生代使用复制算法,老年代使用标记-整理算法
I
多说一句: -XX:ParallelGCThreads=数字N 表示启动多少个GC线程
cpu>8 N =5/8
cpu<8 N=实际个数

7.3.4 Parallel Old

Parallel Old收集器是Parallel Scavenge的老年代版本,使用多线程的标记-整理算法,Parallel Old收集器在JDK1.6才开始提供。

在JDK1.6之前,新生代使用ParllelScavenge收集器只能搭配年老代的Serial Old 收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量。在JDK1.6之前(Parallel Scavenge + Serial Old )

Parallel Old 正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,JDK1.8后可以优先考虑新生代Parallel Scavenge和年老代Parallel Old 收集器的搭配策略。在 JDK1.8及后(Parallel Scavenge + Parallel Old)

JVM常用参数:
-XX:+UseParallelOldGC使用Parallel Old收集器,设置该参数后,新生代Parallel+老年代Parallel Old

7.3.5 并发标记清除GC(CMS)(重要)

CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短。CMS非常适合堆内存大、CPU核数多的服务器端应用,也是G1出现之前大型应用的首选收集器。

Concurrent Mark Sweep并发标记清除,并发收集低停顿,并发指的是与用户线程一起执行

四步过程:

  1. 初始标记(CMS initial mark):只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
  2. 并发标记(CMS concurrent mark)和用户线程一起:进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。主要标记过程,标记全部对象。
  3. 重新标记(CMS remark):为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
  4. 并发清除(CMS concurrent sweep)和用户线程一起:清除GC Roots不可达对象,和用户线程一起工作,不需贾暂停工作线程。基于标记结果,直接清理对象。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。

优点:并发收集停顿低
缺点:

  1. 并发执行,对CPU资源压力大:由于并发进行,CMS在收集与应用线程会同时会增加对堆内存的占用,也就是说,CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大停顿时间
  2. 采用的标记清除算法会导致大量碎片:标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制对堆内存进行压缩。CMS也提供了参数-XX:CMSFullGCsBeForeCompaction(默认O,即每次都进行内存整理)来指定多少次CMS收集之后,进行一次压缩的Full GC。

7.3.5 G1垃圾回收器

  1. G1能充分利用多CPU、多核环境硬件优势,尽量缩短STW。
  2. G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
  3. 宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域(Region),可以近似理解为一个围棋的棋盘。
  4. G1收集器里面将整个的内存区都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的区分,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分Region的集合且不需要Region是连续的,也就是说依然会采用不同的GC方式来处理不同的区域。
  5. G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换。

G1的底层原理

KVM笔试题 kvm常见面试题_职场和发展_07

  1. Region区域化垃圾收集器:最大好处是化整为零,避免全内存扫描,只需要按照区域来进行扫描即可。

G1算法将堆划分为若干个区域(Region),它仍然属于分代收集器。

  • 这些Region的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。
  • 这些Region的一部分包含老年代,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有CMS内存碎片问题的存在了。

在G1中,还有一种特殊的区域,Humongous(巨大的)区域。
如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象默认直接会被分配在年老代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full Gc。

  1. 回收步骤:

针对Eden区进行收集,Eden区耗尽后会被触发,主要是小区域收集+形成连续的内存块,避免内存碎片Eden区的数据移动到Survivor区,假如出现Survivor区空间不够,Eden区数据会部会晋升到Old区

  • Survivor区的数据移动到新的Survivor区,部会数据晋升到Old区;
  • 最后Eden区收拾干净了,GC结束,用户的应用程序继续执行。
  1. 4步过程:
  • 初始标记:只标记GC Roots能直接关联到的对象
  • 并发标记:进行GC Roots Tracing的过程
  • 最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
  • 筛选回收:根据时间来进行价值最大化的回收

常用配置参数

  1. -XX:+UseG1GC
  2. -XX:G1HeapRegionSize=n:设置的G1区域的大小。值是2的幂,范围是1MB到32MB。目标是根据最小的Java堆大小划分出约2048个区域。
  3. -XX:MaxGCPauseMillis=n: 最大cc停顿时间,这是个软目标,IM将尽可能(但不保证)停顿小于这个时间
  4. -XX:InitiatingHeapOccupancyPercent=n: 堆占用了多少的时候就触发Gc,默认为45
  5. -XX:ConcGCThreads=n: 并发GC使用的线程数
  6. -XX:G1ReservePercent=n: 设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险,默认值是10%。

G1与CMS相比的优势:

  1. G1不会产生内存碎片。
  2. 是可以精确控制停顿。该收集器是把整个堆(新生代、老生代)划分成多个固定大小的区域,每次根据允许停顿的时间去收集垃圾最多的区域。

7.3.6 如何选择垃圾收集器

组合的选择

  • 单CPU或小内存,单机程序

-XX:+UseSerialGC

  • 多CPU,需要最大吞吐量,如后台计算型应用-XX:+UseParallelGC或者

-XX:+UseParallelOldGC

  • 多CPU,追求低停顿时间,需快速响应如互联网应用

-XX:+UseConcMarkSweepGC
-XX:+ParNewGC

KVM笔试题 kvm常见面试题_面试_08

7.3.7 JVM GC结合SpringBoot微服务优化

  1. IDEA开发完微服务工程
  2. maven进行clean package
  3. 要求微服务启动的时候,同时配置我们的JVM/GC的调优参数
  4. 公式:

java -server jvm各种参数 -jar jar包名称

7.4 生产环境服务器变慢,诊断思路和性能评估?

7.4.1 整机

top 或者 uptime,系统性能命令精简版,查看load average,如果平均的负载均衡大于0.6说明系统负载很高。

KVM笔试题 kvm常见面试题_KVM笔试题_09

7.4.2 CPU

vmstat 主要用来查看CPU(包括不限于)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xEamzt9S-1636370510084)(http://www.codinglemon.cn/upload/2021/11/image-5eafafe2230c4cd195e3bb4e3c3db26a.png)]

vmstat -n 2 3

一般vmstat工具的使用是通过两个数字参数来完成的,第一个参数是采样的时间间隔数单位是秒,第二个参数是采样的次数

  • procs
  • r:运行和等待CPU时间片的进程数,原则上:1核的CPU的运行队列不要超过2,整个系统的运行队列不能超过总核数的2倍,否则代表系统压力过大
  • b:等待资源的进程数,比如正在等待磁盘I/O、网络I/O等。
  • cpu
  • us:用户进程消耗CPU时间百分比,us值高,用户进程消耗CPU时间多,如果长期大于50%,优化程疗;
  • sy:内核进程消耗的CPU时间百分比;
  • id:处于空闲的CPU百分比.
  • wa:系统等待IO的CPU时间百分比.
  • st:来自于一个虚拟机偷取的CPU时间的百分比

查看额外

  1. 查看所有cpu核信息:mpstat -P ALL 2
  2. 每个进程使用CPU的用量分解信息:pidstat -u 1 -p 进程编号

7.4.3 内存

free -m 应用程序可用内存数

经验值:

  • 应用程序可用内存/系统物理内存>70% 内存充足
  • 应用程序可用内存/系统物理内存<20% 内存不足,需要增加内存
  • 20%<应用程序可用内存/系统物理内存<70% 内存基本够用

查看额外:pidstat -p 进程号 -r 采样间隔秒数

7.4.4 硬盘

df -h 查看硬盘剩余空间

7.4.5 磁盘IO

iostat

KVM笔试题 kvm常见面试题_面试_10

磁盘块设备分布

  • rkB/s每秒读取数据量kB;
  • wkB/s每秒写入数据量kB;
  • svctm I/O请求的平均服务时间,单位毫秒;
    await I/O请求的平均等待时间,单位毫秒;值越小,性能越好;
  • util一秒中有百分几的时间用于I/O操作。接近100%时,表示磁盘带宽跑满,需要优化程序或者增加磁盘;
  • rkB/s、wkB/s根据系统应用不同会有不同的值,但有规律遵循:长期、超大数据读写,肯定不正常,需要优化程序读取。
  • svctm的值与await的值很接近,表示几乎没有IO等待,磁盘性能好,
  • 如果await的值远高于svctm的值,则表示IO队列等待太长,需要优化程序或更换更快磁盘

查看额外:pidstat -d 采样间隔秒数 -p 进程号

7.4.6 网络IO

ifstat 默认本地没有,下载ifstat

KVM笔试题 kvm常见面试题_面试_11

7.5 假如生产环境出现CPU占用过高,请谈谈你的分析思路和定位

结合Linux和JDK命令一起分析

案例步骤:

  1. 先用top命令找出CPU占比最高的
  2. ps -ef或者jps进一步定位,得知是一个怎么样的一个后台程序
  3. 定位到具体的线程或者代码
  1. ps -mp 进程编号 -o THREAD,tid,time 可以查看CPU占用过高的具体线程
  2. 参数解释: -m 显示所有的线程 -p pid 进程使用CPU的时间 -o 该参数后是用户自定义格式
  1. 将需要的线程ID转换为16进制格式(英文小写格式)
  1. printf"%x\n"有问题的线程ID
  1. jstack 进程ID | grep tid(16进制线程ID小写英文) -A60

按以上步骤即可定位到具体出问题的行