Java虚拟机(JVM)面试题3

简介: Java虚拟机(JVM)面试题3

1.Serial收集器

Serial收集器是最基本的、发展历史最悠久的收集器。Serial收集器是单线程回收器,它的单线程意义不仅仅是说它只会使用一个CPU或一个手机线程去完成垃圾收集工作。而且它进行垃圾回收的时候,必须暂停其它所有的工作线程(Stop The World,STW),直到它收集完成。它适合Client模式的应用,在单CPU环境下,它效率高效,由于没有线程交互的开销,专心垃圾收集自然可以获得最高的单线程效率。


串行的垃圾收集器有两种,Serial和Serial Old,一般两者搭配使用。


新生代采用Serial,是利用复制算法;老年代使用Serial Old采用标记-整理算法。Client应用或者命令行程序可以通过-XX:+UseSerialGC开启串行垃圾回收器


2.ParNew收集器

ParNew收集器其实就是Serial收集器的多线程版本。除了使用多线程外其余行为均和Serial收集器一模一样(参数控制、收集算法、Stop The World、对象 分配规则、回收策略等)。


特点:多线程、ParNew收集器默认开启的收集线程数与CPU的数量相同,在CPU非常多的环境中,可以 使用-XX:ParallelgcThreads参数来限制垃圾收集的线程数。


和Serial收集器一样存在Stop The World问题


应用场景:ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为它是除了 Serial收集器外,唯一一个能与CMS收集器配合工作的。


3.Parallel Scavenge 收集器

Parallel Scavenge 收集器与吞吐量关系密切,故也称为吞吐量优先收集器。


特点:属于新生代收集器也是采用复制算法的收集器,又是并行的多线程收集器(与ParNew收集器类似)。 该收集器的目标是达到一个可控制的吞吐量。


还有一个值得关注的点是:gc自适应调节策略(与 ParNew收集器最重要的一个区别)


gc自适应调节策略:Parallel Scavenge收集器可设置-XX:+UseAdptiveSizePolicy参数。


当开关打开时不 需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代 的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动 态设置这些参数以提供最优的停顿时间和最高的吞吐量,这种调节方式称为gc的自适应调节策略。


Parallel Scavenge收集器使用两个参数控制吞吐量:


XX:MaxgcPauseMillis 控制最大的垃圾收集停顿时间


XX:gcRatio 直接设置吞吐量的大小。


4.Serial Old收集器

Serial Old是Serial收集器的老年代版本。


特点:同样是单线程收集器,采用标记-整理算法。


应用场景:主要也是使用在Client模式下的虚拟机中。也可在Server模式下使用。 Server模式下主要的两大用途


在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用。


作为CMS收集器的后备方案,在并发收集Concurent Mode Failure时使用。


5.Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本。


特点:多线程,采用标记-整理算法。


应用场景:注重高吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old 收 集器。


6.CMS收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。


特点:基于标记-清除算法实现。并发收集、低停顿。


应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如 web程序、b/s服务。


CMS收集器的运行过程分为下列4步:


1.初始标记:标记gc Roots能直接到的对象。速度很快但是仍存在Stop The World问题。

2.并发标记:进行gc Roots Tracing 的过程,找出存活对象且用户线程可并发执行。

3.重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记 录。仍然存在Stop The World问题。

4.并发清除:对标记的对象进行清除回收。 CMS收集器的内存回收过程是与用户线程一起并发执行的。

CMS收集器的缺点:


对CPU资源非常敏感。

无法处理浮动垃圾,可能出现Concurrent Model Failure失败而导致另一次Full gc的产生。 因为采用标记-清除算法所以会存在空间碎片的问题,导致大对象无法分配空间,不得不提前触发 一次Full gc。


8.G1收集器

G1收集器一款面向服务端应用的垃圾收集器


特点如下:


1.并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿 时间。部分收集器原本需要停顿Java线程来执行gc动作,G1收集器仍然可以通过并发的方式让Java程序 继续运行。

2.分代收集:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次gc的旧对象以获取更好的收集效果

3.空间整合:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。

4.可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。

关于gc的选择

除非应用程序有非常严格的暂停时间要求,否则请先运行应用程序并允许VM选择收集器(如果没有特别 要求。使用JVM提供给的默认gc就好)。 如有必要,调整堆大小以提高性能。 如果性能仍然不能满足目标,请使用以下准则作为选择收集器的起点:


1.如果应用程序的数据集较小(最大约100 MB),则选择带有选项-XX:+ UseSerialgc的串行收集器。

2.如果应用程序将在单个处理器上运行,并且没有暂停时间要求,则选择带有选项-XX:+UseSerialgc的串行收集器。

3.如果(a)峰值应用程序性能是第一要务,并且(b)没有暂停时间要求或可接受一秒或更长时间的暂停,则让VM选择收集器或使用-XX:+ UseParallelgc选择并行收集器

4.如果响应时间比整体吞吐量更重要,并且垃圾收集暂停时间必须保持在大约一秒钟以内,则选择具有-XX:+ UseG1gc。(值得注意的是JDK9中CMS已经被Deprecated,不可使用!移除该选项)

5.如果使用的是jdk8,并且堆内存达到了16G,那么推荐使用G1收集器,来控制每次垃圾收集的时间。

6.如果响应时间是高优先级,或使用的堆非常大,请使用-XX:UseZgc选择完全并发的收集器。(值得注意的是JDK11开始可以启动Zgc,但是此时Zgc具有实验性质,在JDK15中[202009发布]才取消实验性质的标签,可以直接显示启用,但是JDK15默认gc仍然是G1)

7.这些准则仅提供选择收集器的起点,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处 理器的数量和速度。

8.如果推荐的收集器没有达到所需的性能,则首先尝试调整堆和新生代大小以达到所需的目标。

9.如果性能 仍然不足,尝试使用其他收集器

总体原则:减少STOP THE WORD时间,使用并发收集器(比如CMS+ParNew,G1)来减少暂停时间, 加快响应时间,并使用并行收集器来增加多处理器硬件上的总体吞吐量。


JVM 有哪些垃圾回收算法(回收机制)?

标记-清除算法

标记无用对象,然后进行清除回收。

该算法分为两个阶段,标记和清除。标记阶段标记所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。

优点:实现简单,不需要对象进行移动。

缺点:标记、清除过程效率低,产生大量不连续的内存碎片




复制算法

按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉。

优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。

缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。


标记-整理(压缩)算法

标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。

标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。

优点:解决了标记-清理算法存在的内存碎片问题。

缺点:仍需要进行局部对象移动,一定程度上降低了效率。



分代算法

根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。当前商业虚拟机都采用分代收集的垃圾收集算法


分代算法详细内容看下面一题

介绍一下分代回收机制

JVM分代回收策略

JVM分代回收策略就是Java 虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代、老年代,永久代,不过永久代在JDK1.8永久移除了,被元空间取代了

新生代

新生代主要是用来存放新生的对象。一般占据堆空间的1/3,由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。一般采用的 GC 回收算法是复制算法。 新生代又可以继续细分为 3 部分:Eden、Survivor0(奢外 v额3)(简称 S0)、Survivor1(简称S1)。这 3 部分按照 8:1:1 的比例来划分新生代。当JVM无法为新建对象分配内存空间的时候(Eden区满的时候),JVM触发MinorGc。因此新生代空间占用越低,MinorGc越频繁。MinorGC 触发机制是Eden区满的时候,JVM会触发MinorGC。


详细过程


绝大多数刚刚被创建的对象会存放在 Eden 区(如果新创建的对象占用内存很大则直接分配给老年代)

当 Eden 区第一次满的时候,当Eden区内存不够的时候就会触发一次Minor GC进行一次垃圾回收。首先将 Eden 区的垃圾对象回收清除,并将存活的对象复制到 S0,此时 S1 是空的。

下一次 Eden 区满时,再执行一次垃圾回收。此次会将 Eden 和 S0 区中所有垃圾对象清除,并将存活对象复制到 S1,此时 S0 变为空。

再下一次 Eden 区满时,再执行一次垃圾回收。此次会将 Eden 和 S1区中所有垃圾对象清除,并将存活对象复制到 S0,此时 S1 变为空。

如此反复在 S0 和 S1 之间切换几次(默认 15 次)之后,如果还有存活对象。说明这些对象的生命周期较长,则将它们转移到老年代中。

补充


虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁, 当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置。


老年代

一个对象如果在新生代存活了足够长的时间而没有被清理掉,则会被复制到老年代。老年代的内存大小一般比新生代大,能存放更多的对象。如果对象比较大(比如长字符串或者大数组),并且新生代的剩余空间不足,则这个大对象会直接被分配到老年代上。


我们可以使用 -XX:PretenureSizeThreshold 来控制直接升入老年代的对象大小,大于这个值的对象会直接分配在老年代上。老年代因为对象的生命周期较长,不需要过多的复制操作,所以一般采用标记压缩的回收算法。老年代的对象比较稳定,所以MajorGC不会频繁执行。MajorGC的耗时比较长,因为要先整体扫描再回收,MajorGC会产生内存碎片。为了减少内存损耗,一般需要合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出OOM。


触发MajorGC的条件


1.在进行MajorGC之前,一般都先进行了一次MinorGC,使得有新生代的对象进入老年代,当老年代空间不足时就会触发MajorGC。

2.当无法找到足够大的连续空间分配给新创建的较大对象时,也会触发MajorGC进行垃圾回收腾出空间。

永久代

永久代指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。

Class在被加载的时候元数据信息会放入永久区域,永久代不会发生垃圾回收。所以这也导致永久代的信息会随着类加载的增多而膨胀,最终导致OOM。


注意: 在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。


元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此默认情况下元空间的大小仅仅受本地内存的大小限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中。 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。


FullGC

MajorGC和FullGC的区别

  • Full GC 是清理整个堆空间—包括年轻代和老年代。
  • Major GC 是清理老年代。


Full GC触发机制

System.gc()方法的调用,此方法是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。一般情况下不使用此方法,让虚拟机自己去管理它的内存,可通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc()。

老年代的内存空间不足,发生Full GC一般都会有一次Minor GC。大对象直接进入老年代,如很长的字符串数组,虚拟机提供一个-XX:PretenureSizeThreadhold参数,令大于这个参数值的对象直接在老年代中分配,避免在Eden区和两个Survivor区发生大量的内存拷贝;

方法区内存空间不足,Permanet Generation中存放的为一些class的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:java.lang.OutOfMemoryError: PermGen space

为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。


当Minor GC时,老年代的剩余空间小于历次从新生代往老年代中移的对象的平均内存空间大小时,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。

例如程序第一次触发Minor GC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。


当新生代采用PS GC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。


对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可在启动时通过- java -Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。


堆中分配很大的对象,大对象是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。为了解决这个问题,CMS垃圾收集器提供了一个可配置的参数,即-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程,内存整理的过程无法并发的,空间碎片问题没有了,但提顿时间不得不变长了,JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。


CMS垃圾回收器

1、什么是CMS?

Concurrent Mark Sweep。看名字就知道,CMS是一款并发、使用标记-清除算法的gc。CMS是针对老年代进行回收的GC。

2、CMS有什么用?

CMS以获取最小停顿时间为目的。在一些对响应时间有很高要求的应用或网站中,用户程序不能有长时间的停顿,CMS 可以用于此场景。

3、CMS垃圾清理的过程

总体来说CMS的执行过程可以分为以下几个阶段:


1.初始标记(STW initial mark)

2.并发标记(Concurrent marking)

3.并发预清理(Concurrent precleaning)

4.重新标记(STW remark)

5.并发清理(Concurrent sweeping)

6.并发重置(Concurrent reset)

初始标记 :在这个阶段中,程序中所有的工作线程都将会因为“stop-the-world”(STW)机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出 GC Roots 能直接关联到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里的速度非常快。


并发标记 :在初始标记的基础上继续向下追溯标记(从 Gc Roots 的直接关联对象开始遍历整个对象图的过程),并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿,可以与垃圾收集线程一起并发运行。


并发预清理 :并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段"重新标记"的工作,因为下一个阶段会Stop The World。并发可中止的预清理阶段。这个阶段其实跟上一个阶段做的东西一样,也是为了减少下一个STW重新标记阶段的工作量。增加这一阶段是为了让我们可以控制这个阶段的结束时机,比如扫描多长时间(默认5秒)或者Eden区使用占比达到期望比例(默认50%)就结束本阶段。


重新标记 :这个阶段会暂停虚拟机,收集器线程扫描在CMS堆中剩余的对象。扫描从"根对象"开始向下追溯,标记从新生代晋升的对象、新分配到老年代的对象以及在并发阶段被修改了的对象。(由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录)


并发清理 :清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。


并发重置 :这个阶段,重置CMS收集器的数据结构,等待下一次垃圾回收。


4.CMS缺点

1.CMS回收器采用的基础算法是Mark-Sweep。所有CMS不会整理、压缩堆空间。这样就会有一个问题:经过CMS收集的堆会产生空间碎片。 CMS不对堆空间整理压缩节约了垃圾回收的停顿时间,但也带来的堆空间的浪费。为了解决堆空间浪费问题,CMS回收器不再采用简单的指针指向一块可用堆空 间来为下次对象分配使用。而是把一些未分配的空间汇总成一个列表,当JVM分配对象空间的时候,会搜索这个列表找到足够大的空间来hold住这个对象。

2.需要更多的CPU资源。从上面的图可以看到,为了让应用程序不停顿,CMS线程和应用程序线程并发执行,这样就需要有更多的CPU,单纯靠线程切 换是不靠谱的。并且,重新标记阶段,为空保证STW快速完成,也要用到更多的甚至所有的CPU资源。当然,多核多CPU也是未来的趋势!

3.CMS的另一个缺点是它需要更大的堆空间。因为CMS标记阶段应用程序的线程还是在执行的,那么就会有堆空间继续分配的情况,为了保证在CMS回 收完堆之前还有空间分配给正在运行的应用程序,必须预留一部分空间。也就是说,CMS不会在老年代满的时候才开始收集。相反,它会尝试更早的开始收集,已 避免上面提到的情况:在回收完成之前,堆没有足够空间分配!默认当老年代使用68%的时候,CMS就开始行动了。 – XX:CMSInitiatingOccupancyFraction =n 来设置这个阀值。


总得来说,CMS回收器减少了回收的停顿时间,但是降低了堆空间的利用率。

5.啥时候用CMS

如果你的应用程序对停顿比较敏感,并且在应用程序运行的时候可以提供更大的内存和更多的CPU(也就是硬件牛逼),那么使用CMS来收集会给你带来好处。还有,如果在JVM中,有相对较多存活时间较长的对象(老年代比较大)会更适合使用CMS。

目录
相关文章
|
5天前
|
缓存 监控 Java
Java虚拟机(JVM)性能调优实战指南
在追求软件开发卓越的征途中,Java虚拟机(JVM)性能调优是一个不可或缺的环节。本文将通过具体的数据和案例,深入探讨JVM性能调优的理论基础与实践技巧,旨在为广大Java开发者提供一套系统化的性能优化方案。文章首先剖析了JVM内存管理机制的工作原理,然后通过对比分析不同垃圾收集器的适用场景及性能表现,为读者揭示了选择合适垃圾回收策略的数据支持。接下来,结合线程管理和JIT编译优化等高级话题,文章详细阐述了如何利用现代JVM提供的丰富工具进行问题诊断和性能监控。最后,通过实际案例分析,展示了性能调优过程中可能遇到的挑战及应对策略,确保读者能够将理论运用于实践,有效提升Java应用的性能。 【
37 10
|
3天前
|
监控 算法 Java
深入理解Java虚拟机:JVM调优的实用策略
在Java应用开发中,性能优化常常成为提升系统响应速度和处理能力的关键。本文将探讨Java虚拟机(JVM)调优的核心概念,包括垃圾回收、内存管理和编译器优化等方面,并提供一系列经过验证的调优技巧。通过这些实践指导,开发人员可以有效减少延迟,提高吞吐量,确保应用稳定运行。 【7月更文挑战第16天】
|
7天前
|
NoSQL Java 应用服务中间件
Java高级面试题
Java高级面试题
|
7天前
|
网络协议 安全 前端开发
java面试题
java面试题
|
2天前
|
存储 监控 算法
探索Java虚拟机:深入理解JVM内存模型和垃圾回收机制
在Java的世界中,JVM是核心所在,它不仅承载着代码的运行,还管理着内存资源。本文将带你深入了解JVM的内存模型和垃圾回收机制,通过具体数据与案例分析,揭示它们对Java应用性能的影响,并探讨如何优化JVM配置以提升效率。
|
7天前
|
NoSQL Java 关系型数据库
常见Java面试题
常见Java面试题
|
22天前
|
Unix Linux 虚拟化
虚拟机VMware知识积累
虚拟机VMware知识积累
|
10天前
|
运维 安全 虚拟化
|
2月前
|
存储 SQL 数据挖掘
服务器数据恢复—误删除VMware虚拟机vmdk文件的数据恢复案例
服务器数据恢复环境: 某大厂PS4000服务器,服务器上部署VMware ESXi虚拟化平台。 服务器故障: 机房断电,重启后服务器中的某台虚拟机不能正常启动。管理员查看虚拟机配置文件,发现无法启动的虚拟机的配置文件除了磁盘文件以外其他配置文件全部丢失,xxx-flat.vmdk磁盘文件和xxx-000001-delta.vmdk快照文件还存在。联系VMware原厂工程师进行诊断,VMware原厂工程师尝试新建一个虚拟机,但发现存储空间不足,于是将故障虚拟机下的xxx-flat.vmdk磁盘文件删除了。VMware工程师重新建了一个虚拟机,分配了固定大小的虚拟磁盘,为虚拟机安装了Window
服务器数据恢复—误删除VMware虚拟机vmdk文件的数据恢复案例
|
1月前
|
存储 IDE 开发工具
【读书笔记】 玩转虚拟机基于Vmware+Windows 虚拟化技术
【读书笔记】 玩转虚拟机基于Vmware+Windows 虚拟化技术