大厂面试GC的问题总结

查找算法

        根搜索算法,

       基本思想是:从GC Roots的根节点出发,向下搜索,如果一个对象不能达到GC Roots的时候,说明该对象不再被引用,可以被回收。

       补充概念,在JDK1.2之后引入了四个概念:强引用、软引用、弱引用、虚引用。

       强引用:new出来的对象都是强引用,GC无论如何都不会回收,即使抛出OOM异常。

       软引用:只有当JVM内存不足时才会被回收。

       弱引用:只要GC,就会立马回收,不管内存是否充足。

       虚引用:它作用就是做一些跟踪记录,辅助finalize函数的使用。

 

什么样的类需要被回收:

a.该类的所有实例都已经被回收;

b.加载该类的ClassLoad已经被回收;

c.该类对应的反射类java.lang.Class对象没有被任何地方引用。

 

内存分区

     JVM中的堆一般分为三部分,新生代、老年代和永久代。

  • 1 新生代

主要是用来存放新生的对象。一般占据堆空间的1/3,由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。

新生代分为Eden区、ServivorFrom、ServivorTo三个区。

    • Eden区:Java新对象的出生地(如果新创建的对象占用内存很大则直接分配给老年代)。当Eden区内存不够的时候就会触发一次MinorGc,对新生代区进行一次垃圾回收。
    • ServivorTo:保留了一次MinorGc过程中的幸存者。
    • ServivorFrom: 上一次GC的幸存者,作为这一次GC的被扫描者。

当JVM无法为新建对象分配内存空间的时候(Eden区满的时候),JVM触发MinorGc。因此新生代空间占用越低,MinorGc越频繁。MinorGC采用复制算法。

  • 2 老年代

老年代的对象比较稳定,所以MajorGC不会频繁执行。

触发MinorGC的条件(清理老年代):

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

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

MajorGC采用标记—清除算法(或者标记—整理算法)

MajorGC的耗时比较长,因为要先整体扫描再回收,MajorGC会产生内存碎片。为了减少内存损耗,一般需要合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM。

  • 3永久代

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

Class在被加载的时候元数据信息会放入永久区域,但是GC不会在主程序运行的时候清除永久代的信息。所以这也导致永久代的信息会随着类加载的增多而膨胀,最终导致OOM。

 

 

Full GC触发机制:(是清理整个堆空间—包括年轻代和老年代)

1 调用System.gc时,系统建议执行Full GC,但是不必然执行

2 老年代空间不足

3 方法区空间不足

4 通过Minor GC后进入老年代的平均大小大于老年代的可用内存

5 由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,

4 当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载。

 

怎么晋升老年代

Java中对象一般在新生代中进行创建,但是在一定的机制中会触发晋升老年代的机制。相关机制包括:

1、分配担保机制

    Eden区满时,进行Minor GC,当Eden和一个Survivor区中依然存活的对象无法放入到Survivor中,则通过分配担保机制提前转移到老年代中。 

2、对象过大

    若对象体积太大,新生代无法容纳这个对象,就会绕过新生代, 直接在老年代分配, 此参数只对Serial及ParNew两款收集器有效。

    参数-XX:PretenureSizeThreshold用来设置这个门限值。

3、长期存活的对象

    对象头的Mark Word中包含对象的年龄。当年龄增加到一定的临界值时,就会晋升到老年代中。

    该临界值由参数:-XX:MaxTenuringThreshold来设置,默认为15,即对象在经历15次minor gc后会晋升到老年代。

4、动态对象年龄判定

    如果在Survivor区中相同年龄的对象的所有大小之和超过Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。

 

GC算法

       常见的GC算法:复制、标记-清除和标记-压缩

 

       复制:复制算法采用的方式为从根集合进行扫描,将存活的对象移动到一块空闲的区域,当存活的对象较少时,复制算法会比较高效(新生代的Eden区就是采用这种算法),其带来的成本是需要一块额外的空闲空间和对象的移动。

       标记-清除:该算法采用的方式是从跟集合开始扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,并进行清除。

清除阶段清理的是没有被引用的对象,存活的对象被保留。

标记-清除动作不需要移动对象,且仅对不存活的对象进行清理,在空间中存活对象较多的时候,效率较高,但由于只是清除,没有重新整理,因此会造成内存碎片。

       标记-压缩:该算法与标记-清除算法类似,都是先对存活的对象进行标记,但是在清除后会把活的对象向左端空闲空间移动,然后再更新其引用对象的指针,

 

垃圾收集器

       在JVM中,GC是由垃圾回收器来执行,所以,在实际应用场景中,我们需要选择合适的垃圾收集器,下面我们介绍一下垃圾收集器。

 

4.1 串行收集器(Serial GC)

       Serial GC是最古老也是最基本的收集器,但是现在依然广泛使用,JAVA SE5和JAVA SE6中客户端虚拟机采用的默认配置。比较适合于只有一个处理器的系统。在串行处理器中minor和major GC过程都是用一个线程进行回收的。它的最大特点是在进行垃圾回收时,需要对所有正在执行的线程暂停(stop the world),对于有些应用是难以接受的,但是如果应用的实时性要求不是那么高,只要停顿的时间控制在N毫秒之内,大多数应用还是可以接受的,而且事实上,它并没有让我们失望,几十毫秒的停顿,对于我们客户机是完全可以接受的,该收集器适用于单CPU、新生代空间较小且对暂停时间要求不是特别高的应用上,是client级别的默认GC方式。

4.2 ParNew GC

       基本和Serial GC一样,但本质区别是加入了多线程机制,提高了效率,这样它就可以被用于服务端上(server),同时它可以与CMS GC配合,所以,更加有理由将他用于server端。

4.3 Parallel Scavenge GC

       在整个扫描和复制过程采用多线程的方式进行,适用于多CPU、对暂停时间要求较短的应用,是server级别的默认GC方式。

4.4 CMS (Concurrent Mark Sweep)收集器

       该收集器的目标是解决Serial GC停顿的问题,以达到最短回收时间。常见的B/S架构的应用就适合这种收集器,因为其高并发、高响应的特点,CMS是基于标记-清楚算法实现的。

CMS收集器的优点:并发收集、低停顿,但远没有达到完美;

CMS收集器的缺点:

a.CMS收集器对CPU资源非常敏感,在并发阶段虽然不会导致用户停顿,但是会占用CPU资源而导致应用程序变慢,总吞吐量下降。

b.CMS收集器无法处理浮动垃圾,可能出现“Concurrnet Mode Failure”,失败而导致另一次的Full GC。

c.CMS收集器是基于标记-清除算法的实现,因此也会产生碎片。

4.5 G1收集器

       相比CMS收集器有不少改进,首先,基于标记-压缩算法,不会产生内存碎片,其次可以比较精确的控制停顿。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值