垃圾回收篇~~面试题集

9 篇文章 1 订阅

1、大厂面试题

蚂蚁金服

你知道哪几种垃圾回收器,各自的优缺点,重点讲一下cms和G1?
JVM GC算法有哪些,目前的JDK版本采用什么回收算法?
G1回收器讲下回收过程GC是什么?为什么要有GC?
GC的两种判定方法?CMS收集器与G1收集器的特点

百度

说一下GC算法,分代回收说下
垃圾收集策略和算法

天猫

JVM GC原理,JVM怎么回收内存
CMS特点,垃圾回收算法有哪些?各自的优缺点,他们共同的缺点是什么?

滴滴

Java的垃圾回收器都有哪些,说下g1的应用场景,平时你是如何搭配使用垃圾回收器的

京东

你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,
包括原理,流程,优缺点。垃圾回收算法的实现原理

阿里

讲一讲垃圾回收算法。
什么情况下触发垃圾回收?
如何选择合适的垃圾收集算法?
JVM有哪三种垃圾回收器?

字节跳动

常见的垃圾回收器算法有哪些,各有什么优劣?
System.gc()和Runtime.gc()会做什么事情?
Java GC机制?GC Roots有哪些?
Java对象的回收方式,回收算法。
CMS和G1了解么,CMS解决什么问题,说一下回收的过程。
CMS回收停顿了几次,为什么要停顿两次?

2、 面试题解析

2.1、你知道哪几种垃圾回收器,各自的优缺点,重点讲一下cms和G1?

Serial:采用复制算法、串行回收和"stop-the-World"机制的方式执行内存回收。除了年轻代之外,Serial收集器还提供用于执行老年代垃圾收集的Serial old收集器。Serial old收集器同样也采用了串行回收和"stop the World"机制,只不过内存回收算法使用的是标记-压缩算法。优点:简单而高效(Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中,可用内存一般不大(几十MB至一两百MB),可以在较短时间内完成垃圾收集(几十ms至一百多ms),只要不频繁发生,使用串行回收器是可以接受的。缺点:如果垃圾回收的时间过长,就会造成应用卡顿的情况。

ParNew:可以理解为serial收集器的多线程版本。除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"stop-the-World"机制。优点:相比于serial 垃圾收集器,它对于新生代,回收次数频繁,使用并行方式相对高效。而不是都采用串行的方式。缺点:虽然和serial 相比 ,已经采用了并行的方式,不过还是 stop-the-world的机制,所以一旦垃圾回收过于频繁,也会有应用卡顿的情况。

Parallel:Parallel Scavenge收集器同样也采用了复制算法、并行回收和"Stop the World"机制。优点:和ParNew收集器不同,ParallelScavenge收集器的可以通过参数控制的吞吐量、自适应调节策略也是它的一个特点在自适应调节策略下,年轻代的大小、Eden和Survivor的比例、晋升老年代的对象年龄等参数会被自动调整,在手动调优比较困难的场合,可以直接使用这种自适应的方式高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。因此,常见在服务器环境中使用。例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序。Parallel收集器在JDK1.6时提供了用于执行老年代垃圾收集的Parallelold收集器,用来代替老年代的serialold收集器。Parallel old收集器采用了标记-压缩算法,但同样也是基于并行回收和"stop-the-World"机制。

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。适用于希望系统停顿时间短,给用户良好体验的引用。CMS收集器是基于 "标记-清除"算法实现的,它的运作过程分为四个步骤:初始标记(CMS initial mark):标记 GC Roots 能直接关联到的对象。并发标记(CMS concurrent mark):进行 GC Roots Tracing。重新标记(CMS remark):修正并发标记期间的变动部分。并发清除(CMS concurrent sweep)
其中初始标记、重新标记这两个步骤仍然需要 暂停其它工作线程。初始标记只是标记GCRoot能关联的对象过程很快。并发标记阶段进行GCRoot Tracing 时间较长,重新标记这个阶段停顿的时间比初始标记阶段的时间要长,但远比并发标记阶段的停顿时间要短。
优点:并发收集、低延迟
缺点:1、会产生内存碎片,导致并发清除后,用户线程可用的空间不足。在无法分配大对象的情况下,不得不提前触发FullGC。2、CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。3、CMS收集器无法处理浮动垃圾。可能出现“Concurrent Mode Failure"失败而导致另一次Full GC的产生。在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行GC时释放这些之前未被回收的内存空间。

G1收集器: 垃圾收集器理论进一步发展的产物,一是基于 “标记-整理” 算法实现的收集器。也就是说不会产生空间碎片。二是它可以非常精确的控制停顿。 G1收集器可以实现在基本不牺牲吞吐量的前提下完成低提顿的内存回收,因为它极力避免全区域的垃圾回收,G1将整个Java堆(新生代、老年代)划分为多个大小固定的独立区域,并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域。
运作步骤:初始标记(Initial Marking)并发标记(Concurrent Marking)最终标记(Final Marking)筛选回收(Live Data Counting and Evacuation)

优点:与其他GC收集器相比,G1使用了全新的分区算法,
并行性:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。
并发性:G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况和之前的各类回收器不同,它同时兼顾年轻代和老年代。CMS:“标记-清除”算法、内存碎片、若干次GC后进行一次碎片整理G1将内存划分为一个个的region。内存的回收是以region作为基本单位的。Region之间是复制算法,但整体上实际可看作是标记-整理(Mark-Compact)算法,两种算法都可以避免内存碎片。

缺点:相较于CMS,比如在用户程序运行过程中,G1无论是为了垃圾收集产生的内存占用还是程序运行时的额外执行负载都要比CMS要高。从经验上来说,在小内存应用上CMS的表现大概率会优于G1,而G1在大内存应用上则发挥其优势。平衡点在6-8GB之间。

2.2、JVM GC算法有哪些,目前的JDK版本采用什么回收算法?

标记-清除算法最基础的算法,分为标记和清除两个阶段:首先标记出所有需要回收的对象,完成后统一回收掉所有被标记的对象。后续的算法都是基于此算法来改进的。主要有两个缺点:效率问题:标记和清除的过程效率都不高。空间问题:标记清除之后会产生大量不连续的内存碎片,这样可能会导致以后再运行过程中需要分配大对象时无法找到足够的连续内存而不得不触发新的一次垃圾回收。

复制算法为了解决效率问题,复制算法出现了。将内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存使用完了,就讲存活着的对象复制到另一块上面,然后把使用过的内存空间一次清理掉。这样每次都是对一块内存进行回收,分配时也不考虑内存碎片等复杂情况。代价就是将内存缩小为原来的一半。

标记-整理算法复制收集算法在对象存活率较高时需要执行很多的复制操作,效率很低。所以在老年代里一般不能直接选用这种算法。标记整理算法的标记过程和标记清除算法是一样,后续不是直接对可回收对象进行清除,而是将所有存活的对象都向一端移动,然后清理掉端边界以外的内存。

分代收集算法当前商业虚拟机的垃圾收集都采用 “分代收集” 算法,这种算法就是根据对象的存活周期的不同将内存划分为几块。一般是把Java对分为新生代和老年代,这样就根据各个年代的特点采用适当的收集算法。在新生代中,每次垃圾回收时都有大量的对象死亡,只有少量的存活,所以选用复制算法,只需要付出少量的存活的复制成本就可以完成回收。在老年代中,因为对象的存活率高、没有额外空间对它进行分配担保,那就必须使用"标记-清理" 的算法进行回收。

jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)jdk1.9 默认垃圾收集器G1【从局部(两个Region之间)来看是基于"标记—复制"算法实现,从整体来看是基于"标记-整理"算法实现】JDK 11当中,加入了实验性质的ZGC。它的回收耗时平均不到2毫秒。它是一款低停顿高并发的收集器。ZGC几乎在所有地方并发执行的,除了初始标记的是STW的。所以停顿时间几乎就耗费在初始标记上,这部分的实际是非常少的。那么其他阶段是怎么做到可以并发执行的呢?ZGC主要新增了两项技术,一个是着色指针Colored Pointer,另一个是读屏障Load Barrier。ZGC 是一个并发、基于区域(region)、增量式压缩的收集器。Stop-The-World 阶段只会在根对象扫描(root scanning)阶段发生,这样的话 GC 暂停时间并不会随着堆和存活对象的数量而增加。

2.3、G1回收器讲下回收过程,GC是什么?为什么要有GC?

G1抛弃了之前的分代收集的方式,面向整个堆内存进行回收,把内存划分为多个大小相等的独立区域Region。
一共有4种Region:自由分区Free Region年轻代分区Young Region,年轻代还是会存在Eden和Survivor的区分老年代分区Old Region大对象分区Humongous Region
每个Region的大小通过-XX:G1HeapRegionSize来设置,大小为1~32MB,默认最多可以有2048个Region,那么按照默认值计算G1能管理的最大内存就是32MB*2048=64G。
对于大对象的存储,存在Humongous概念,对G1来说,超过一个Region一半大小的对象都被认为大对象,将会被放入Humongous Region,而对于超过整个Region的大对象,则用几个连续的Humongous来存储
G1的回收过程分为以下四个步骤:初始标记:标记GC ROOT能关联到的对象,需要STW并发标记:从GCRoots的直接关联对象开始遍历整个对象图的过程,扫描完成后还会重新处理并发标记过程中产生变动的对象最终标记:短暂暂停用户线程,再处理一次,需要STW筛选回收:更新Region的统计数据,对每个Region的回收价值和成本排序,根据用户设置的停顿时间制定回收计划。再把需要回收的Region中存活对象复制到空的Region,同时清理旧的Region。需要STW。

GC是垃圾收集的意思。忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。

2.4、GC的两种判定方法?CMS收集器与G1收集器的特点

1、引用计数算法 给对象中增加一个引用计数器,每当一个地方引用它时,计数器值就加1;当引用失效,计数器值就建1;计算器为0的对象就是不可能再被使用的。 引用计数算法的实现简单,判断效率也很高,但是它很难解决对象之间的相互循环引用的问题。所以Java没有选用引用计数算法来管理内存。2、根搜索算法 通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索的路径称为引用链,当一个对象在“GC Roots”没有任何引用链相连的话,也就是GC Roots到这个对象不可达时,证明此对象已经不可用,可以被回收了。可作为GC roots的对象的包括下面几种:1>栈中的对象引用、2>方法区中常量的引用、3>方法区中静态对象的引擎、4>本地方法区中native对象的引用

2.5、垃圾收集策略和算法

2.6、什么情况下触发垃圾回收?

1、新生代GC(Minor GC/Scavenge GC):发生在新生代的垃圾收集动作。因为Java对象大多具有朝生夕灭的特性,因此Minor GC非常频繁(不一定等Eden区满了才触发),一般回收速度也比较快。在新生代中,每次垃圾收集时会发现有大量对象死去,只有少量存活2、执行 system.gc()的时候3、老年代空间不足,一次Full GC 之后,然后不足 会触发 java.outofmemoryError:java heap space4、永久代空间不足 永生代或者永久代, java.outofMemory PerGen Space5、minor之后 survior放不下,放入老年代,老年代也放不下,触发FullGC, 或者新生代有对象放入老年代,老年代放不下,触发FullGC6、新生代晋升为老年代时候,老年代剩余空间低于新生代晋升为老年代的速率,会触发老年代回收7、new 一个大对象,新生代放不下,直接到老年代,空间不够,触发FullGC。

2.7、System.gc()和Runtime.gc()会做什么事情?

java.lang.System.gc()只是java.lang.Runtime.getRuntime().gc()的简写,两者的行为没有任何不同。都会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。JVM实现者可以通过system.gc()调用来决定JVM的GC行为。而一般情况下,垃圾回收应该是自动进行的,无须手动触发,否则就太过于麻烦了。然而System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用(无法保证马上触发GC)

2.8、CMS回收停顿了几次,为什么要停顿两次?

需要停顿2次。初始标记、重新标记这两个步骤 需要 暂停其它工作线程。不过初始标记只是标记GCRoot能关联的对象过程很快。并发标记阶段进行GCRoot Tracing 时间较长,重新标记这个阶段停顿的时间比初始标记阶段的时间要长,但远比并发标记阶段的停顿时间要短。至于为什么要停顿2次,我的理解是:第一次初始标记的过程是很快的,这样处理可能是效率更快一些,然后,在并发标记阶段,会运行并更新引用字段,所以不能保证在并发标记阶段结束时标记所有活动对象。为了处理这个问题,应用程序再次停止第二次暂停,它通过重新访问在并发标记阶段修改的对象来完成标记。由于备注暂停比初始标记更重要,因此多个线程并行运行以提高其效率。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值