大数据面试宝典之JVM篇

猿天歌

入职一线大厂不光关注技术的宽广度,还需要考察技术的深度,尤其是现在技术内卷的时代,应对大部分公司面试时JVM都是我们必备技能。经过长时间打磨,总结出JVM全面面试题,涵盖JVM所有知识点,不仅对您面试有所帮助,对您个人对JVM的深入了解亦有助力。通过这个JVM面试题专栏,让面试更加轻松,不再有所顾虑。 整个栏目对内存、堆、栈、垃圾回收、字节码与类的加载、性能监控以及调优等等都有应对面试题。 希望大家有所收获,也希望大家能够喜欢,多多支持,后期我会持续给大家带来更多的面试题专栏。

  1. 28/12/2021

    87.可以具体说说G1收集器的适用场景以及它的垃圾回收过程嘛?

    适用场景: 面向服务端应用,针对具有大内存、多处理器的机器。 最主要的应用是需要低GC延迟,并具有大堆的程序 Hotspot垃圾收集器中,除了G1以外,其它垃圾收集器使用内置的JVM线程执行GC的多线程操作,而G1 GC可以采用应用线程承担后台运行的GC工作,即当JVM的GC线程处理速度慢时,系统调用应用程序线程帮助加速垃圾回收过程。 G1回收器垃圾回收过程: G1 GC的垃圾回收过程主要包括三个环节:年轻代GC、老年代并发标记过程、混合回收、如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。 回收过程一:年轻代GC。JVM启动时,G1 先准备好Eden区,程序在运行过程中不断创建对象到Eden区,当年轻代的Eden区用尽时,G1会启动一次年轻代垃圾回收过程,G1的年轻代收集阶段是一个并行的独占式收集器。在年轻代回收期,年轻代垃圾回收只会回收Eden区和Survivor区,G1 GC暂停所有应用程序线程的执行,并创建回收集,回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。然后开始回收过程: 第一阶段,根扫描。根是指static变量指向的对象,正在执行的方法调用链条上的局部变量等。根引用连同RSet记录的外部引用作为扫描存活对象的入口。 第二阶段,更新RSet。处理dirty card queue中的card,更新RSet。此阶段完成后,RSet可以准确的反映老年代对所在的内存分段中对象的引用。 第三阶段,处理RSet。识别被老年代对象指向的Eden区中的对象,这些被指向的Eden中的对象被认为是存活的对象。 第四阶段,复制对象。此阶段,对象树被遍历,Eden区内存段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区内存段中活的对象如果年龄未到阈值,年龄会加1,达到阈值会被复制到Old区中空的内存分段。如果Survivor空间不够,Eden空间的部分数据会直接晋升到老年代空间。 第五阶段,处理引用。处理Soft,Weak,Phantom,Final,JNI Weak等引用。最终Eden空间的数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程中可以达到内存整理的效果,减少碎片。 回收过程二:并发标记过程。 初始标记阶段(Root Region Scanning)。标记从根节点直接可达的对象。这个阶段是STW的,并且会触发一次年轻代GC。 根区域扫描。G1 GC扫描Survivor区直接可达的老年代区域对象,并标记被引用的对象。这一过程必须在Young GC之前完成。 并发标记(Concurrent Marking)。在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那这个区域会被立即回收。同时,并发标记过程中,会计算区域的对象活性(区域中存活对象的比例)。 再次标记(Remark)。由于应用程序持续进行,需要修正上一次的标记结果。是STW的。G1中采用了比CMS更快的初始快照算法SATB(snapshot-at-the-beginning)。 独占清理(cleanup,STW)。计算各个区域的存活对象和GC回收比例,并且进行排序,识别可以混合回收的区域。为下阶段做铺垫。是STW的。这个阶段并不会实际上去做垃圾的收集。 并发清理阶段。识别并清理完全空闲的区域。 回收过程三: 当越来越多的对象晋升到老年代Old Region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region。需要注意的是:一部分老年代,而不是全部老年代。可以选择哪些Old Region进行收集,从而可以对垃圾回收的耗时时间进行控制。也需要注意的是Mixed GC并不是Full GC。 并发标记结束后,老年代中百分百为垃圾的内存分段被回收了,部分为垃圾的内存分段被计算出来。默认情况下,这些老年代的内存分段会分8次(可以通过-XX:G1MixedGCCountTarget设置)被回收。 混合回收的回收集(Collection Set)包括八分之一的老年代内存分段,Eden区内存分段,Survivor区内存分段。混合回收的算法和年轻代回收的算法完全一样,只是回收集多了老年代的内存分段。 由于老年代中的内存分段默认分8次回收,G1会优先回收垃圾多的分段。垃圾占内存分段比例越高的,越先回收。并且有一个阈值会决定内存分段是否被回收,-XX:G1MixedGCLiveThresholdPercent,默认65%,就是垃圾占内存分段比例要达到65%才会被回收。如果垃圾占比太低,意味着存活的对象占比高,在复制的时候会花费更多的时间。 混合回收并不一定要进行8次。有一个阈值-XX:G1HeapWastePercent,默认值为10%,就是允许整个堆内存中有10%的空间被浪费,也就是如果发现可以回收的垃圾占堆内存的比例低于10%,则不再进行混合回收。因为GC会花费很多时间,但是会收到的内存却很少。 举例:一个Web服务器,Java进程最大堆内存为4G,每分钟响应1500个请求,每45秒会新分配大约2G内存。G1会每45s进行一次年轻代回收,每31小时整个堆的使用率会达到45%,会开始老年代并发标记过程,标记完成后,开始四到五次的混合回收。 回收过程四:Full GC G1的初衷就是要避免Full GC的出现,但是如果上述工作方式不能正常工作,G1会停止应用程序的执行(STW),使用单线程的内存回收算法进行垃圾回收,性能会非常差,应用程序停顿时间会很长。 要避免Full GC的发生,一旦发生需要进行调整。当堆内存太小,就容易发生Full GC,当G1在复制存活对象时,没有空闲的内存分段可用,则会回退到Full GC,这种情况下,可以增大内存来解决。 导致G1 Full GC的原因可能有两个: 1.Evacuation的时候没有足够的to-space来存放晋升的对象。 2.并发处理过程完成之前空间耗尽。

    7 min
  2. 28/12/2021

    86.可以详细谈谈G1 垃圾回收器吗?

    由于应用程序所对应的业务越来越庞大、复杂、用户也越来越多,没有GC就不能保证程序正常进行,而经常造成STW的GC跟不上实际的需求,并且适应现在不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间,且兼顾良好的吞吐量,就出现了G1垃圾收集器,全称Garbage First GC。起这个名字是因为G1是一个并行回收器,它把内存分割为很多不相关的区域,使用不同的区域来表示Eden、幸存者0区、幸存者1区,老年代等。G1 GC有计划的避免在整个Java堆中进行全区域的垃圾收集,它跟踪各个区域里面的垃圾堆积的价值大小,在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的区域。侧重点在于回收垃圾最大的区域。 G1是一款面向服务端应用的垃圾收集器,主要针对配备多核CPU及大容量内存的机器,以极高概率满足GC停顿时间的同时,还兼顾高吞吐量的性能特征。 G1回收器的特点(优势与不足): 并行与并发。 并行性:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程STW。 并发性:G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般不会在整个回收阶段发生完全阻塞应用程序的情况。 分代收集。 从分代上看,G1依然属于分代型垃圾回收器,它会区分年轻代和老年代,年轻代依然有Eden区和Survivor区。但从堆的结构上看,它不要求整个Ed嗯区、年轻代或者老年代都是连续的,也不再坚持固定大小和固定数量。 将堆空间分为若干个区域,这些区域中包含了逻辑上的年轻代和老年代。 和之前的各类回收器不同,它同时兼顾年轻代和老年代。 空间整合。 G1内存划分为一个个的区域(region)。内存的回收是以region作为基本单位的。Region之间是复制算法,但整体上是标记-压缩算法,这两种算法都可以避免内存碎片。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一个GC。尤其当Java堆内存非常大时,G1优势更明显。 可预测的停顿时间模型。 这是G1相对于CMS的另一大优势,G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过M毫秒。 由于分区的原因,G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制。 G1跟踪各个Region里面的垃圾堆积的价值(回收所获得的空间大小以及回收所需要时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。保证了G1收集器在有限时间内可以获取尽可能最高的收集效率。 相较于CMS,G1还不具备全方位、压倒性的优势。比如在用户线程运行过程中,G1无论是为了垃圾收集产生的内训占用还是运行时的额外执行负载都要比CMS要高。在小内存应用上CMS的表现大概率要优于G1,而G1在大内存应用上则发挥其优势。

    4 min
  3. 27/12/2021

    85.CMS 垃圾回收器可以详细说说吗?

    CMS 垃圾回收器主打的是低延迟,在JDK1.5时,Hotspot推出了强交互应用的垃圾收集器,也就是CMS收集器,全称Concurrent-Mark-Sweep。它是Hotspot虚拟机中第一款真正意义上的并发收集器,它第一次实现了垃圾收集线程与用户线程同时工作。 CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短(低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。CMS 垃圾收集器采用标记-清除算法并且也会有STW机制。 但是,CMS 垃圾收集器无法和JDK1.4中已经存在的新生代收集器Parallel Scavenge配合使用,所以JDK 1.5中使用CMS来收集老年代的时候,新生代只能选择ParNew或Serial收集器中的一个。 工作原理: CMS整个过程比较复杂,分为4个主要阶段,即初始标记阶段、并发标记阶段、重新标记阶段和并发清除阶段。 初始标记阶段:在这个阶段中,程序所有工作线程都将会因为STW机制而出现短暂的暂停,这个阶段主要任务仅仅是标记出GC Roots能直接关联到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里速度非常快。 并发标记阶段:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。 重新标记阶段:由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。 并发清除阶段:这个阶段清理删除掉标记阶段判断的已经死亡的对象,释放空间。由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。 尽管CMS收集器采用的是并发回收,但是在其初始化标记和再次标记这两个阶段中任需要执行STW机制来暂停程序中的工作线程,不过暂停时间不长;由于最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的。此外,由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此,CMS收集器不能像其他收集器那样等到老年代几乎被完全填满再进行收集,而是当堆内存使用率达到某一阈值时,就开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿就长了。 CMS收集器的垃圾收集算法采用的是标记-清除算法,不可避免的会产生一些内存碎片。那么CMS在为新对象分配内存空间时,将无法使用指针碰撞技术,只能选择空闲列表执行内存分配。 JDK 9时,CMS被标记了将会弃用,到JDK14时,删除了CMS垃圾收集器。 总的来说,CMS收集器的优缺点。 优点: 并发收集 低延迟 缺点: 会产生内存碎片 CMS收集器对CPU资源非常敏感。 CMS收集器无法处理浮动垃圾。

    5 min

About

入职一线大厂不光关注技术的宽广度,还需要考察技术的深度,尤其是现在技术内卷的时代,应对大部分公司面试时JVM都是我们必备技能。经过长时间打磨,总结出JVM全面面试题,涵盖JVM所有知识点,不仅对您面试有所帮助,对您个人对JVM的深入了解亦有助力。通过这个JVM面试题专栏,让面试更加轻松,不再有所顾虑。 整个栏目对内存、堆、栈、垃圾回收、字节码与类的加载、性能监控以及调优等等都有应对面试题。 希望大家有所收获,也希望大家能够喜欢,多多支持,后期我会持续给大家带来更多的面试题专栏。