垃圾回收机制

1.JVM运行时数据分区有哪些?分别存储哪些内容?有什么特点?

2.垃圾回收主要回收哪些区域?

垃圾回收主要关注的是Java堆和方法区。

3.如何判断一个对象是否可回收

如果一个对象已死,那么就对其进行回收。
判断对象存活的算法:
1.引用计数算法。
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
通常情况下,引用计数算法虽然占用了一些额外的内存空间来进行计数,但它的原理简单,判定效率也很高。
不过在Java主流的虚拟机中都没有选用计数算法,因为这个引用计数算法,没法解决对象之间相互循环引用等问题。
2.可达性分析算法
当前主流的商用程序语言(Java、C#)的内存管理子系统,都是通过可达性分析(Reachablity Analysis)算法来判定对象是否存活的。
基本思路:通过一系列称为GC Roots的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为引用链,如果某个对象到GC Roots直接没有任何引用链相连,也就是这个对象不可达,则证明此对象是不可能被使用的。
在这里插入图片描述

4.可达性分析后发现一个对象在引用链上,那他一定不会被回收吗

针对引用可分为:

  • 强引用:只要强引用关系还存在,垃圾回收器永远不会回收到被引用的对象。
  • 软引用SoftReference:描述一些还有用,但非必须的对象。在系统要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
  • 弱引用WeakReference:非必须对象,比软引用更弱。被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
  • 虚引用:也称为幽灵引用或者幻影引用。最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间产生影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

5.可达性分析后发现一个对象不在引用链上,那它一定会被回收吗?

即使被可达性分析算法判定为不可达的对象,也不一定被回收。
真正宣告一个对象的死亡,至少要经历两次标记过程。
如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。假如对象没有覆盖finalize()方法,或者finalized()方法已经被虚拟机调用过,那么虚拟机将这两种情况视为没有必要执行。
如果这个对象被判定为确有必要执行finalize()方法,那么该对象将会被放置在一个名为F-Queue的队列之中,并在稍后由一条虚拟机自动建立的,低调度优先级的Finalizer线程去执行它们的finalize()方法。
这里说的执行是虚拟机会触发这个方法开始运行,但并不承诺一定会等待它运行结束。这样做的原因是,如果某个对象的finalize()方法执行缓慢,甚至发生了死循环,将可能导致F-Queue队列中的其他对象永久处于等待,甚至导致整个内存回收子系统的奔溃。finalize()方法是对象逃脱死亡命运的最后机会,稍后收集器将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中拯救自己,只要重新与引用链上的任何一个对象建立关联即可,譬如把自己复制给某个类变量或者对象的成员变量,那么它将被移出即将回收的集合,如果对象这时候还没有逃脱,那么基本上它真的要被回收了。
注意:一个对象的finalized()方法只会被执行一次。

6.垃圾回收算法有哪些?

  • 标记-清除算法。
    首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来标记存活的对象,统一回收所有未被标记的对象。
    主要缺点:
    1.执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清楚的动作,导致标记和清楚两个过程执行效率都随数量增长而降低。
    2.内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会 导致当以后在程序运行过程中需要分配大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
    在这里插入图片描述
  • 标记-复制算法:
    将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次性清理掉。
    缺点:这种算法会产生大量的内存间复制的开销,但对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。
    在这里插入图片描述
    现在商用的Java虚拟机大多都优先采用了这种收集算法去回收新生代。
  • 标记-整理算法:
    标记-复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。而在老年代中,存活率较高。所以不推荐采用标记-复制算法。
    针对老年代的存亡特征,提出了标记-整理算法。其标记过程与标记清除算法一样,但是后续并不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间的一端移动,然后直接清理掉边界以外的内存。
    标记清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。
    在这里插入图片描述
    移动这些操作时候必须全程暂停用户程序才能进行。
    如果标记-清除算法那样不考虑移动和整理存活对象,弥散于堆中的存活对象导致的空间碎片化问题就只能依赖更复杂的内存分配器和内存访问器来解决。

7.常用的垃圾回收器有哪些?

在这里插入图片描述

  • Serial收集器
    Serial收集器是最基础的历史最悠久的收集器。他是一个单线程工作的收集器,并且在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。
    新生代采用的是复制算法。老年代采用标记-整理算法。都需要暂停所有用户线程。
    在这里插入图片描述
  • ParNew收集器
    ParNew收集器实质上是Serial收集器的多线程并行版本,除了同时使用多条线程进行收集之外,其余的行为与Serial收集器完全一致。
    新生代采用复制算法,老年代采用标记-整理算法。
    在这里插入图片描述
  • Paraller Scavenge收集器
    基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器。
    Paraller Scavenge收集器的目标是达到一个可控制的吞吐量。
  • Serial Old收集器
    Serial Old是Serial收集器的老年代版本,他同样是一个单线程收集器,使用标记-整理算法。这个收集器的主要意义也是供客户端模式下的HotSpot虚拟机使用。如果在服务端模式下,要么在JDK5之前与Paraller Scavenge收集器搭配使用,要么作为CMS收集器失败后的后备方案。
    在这里插入图片描述
  • Parllel Old收集器
    Paraller Old是Paraller Scavenge收集器的老年代版本,支持多线程并发收集,基于标记整理算法实现,直到JDK1.6才提供,在此之前Paraller Scavenge收集器只有Serial Old收集器才能配合。
    在这里插入图片描述
  • CMS收集器
  • Concurrent Mark Sweep
    CMS收集器是一种以获取最短回收停顿时间为目标的收集器。
    基于标记-清除算法实现的。
    分为四个步骤:
    1.初始标记
    2.并发标记
    3.重新标记
    4.并发清除
    其中初始标记和重新标记这两个阶段仍然要Stop the World。
    初始标记只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但不需要停顿用户线程,可以与垃圾收集线程一起并发运行;而重新标记阶段则是为了修正并发标记期间,因为用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;最后是并发清除阶段,清理删除掉标记阶段的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发。
    在这里插入图片描述
  • Garbage First收集器G1
    四个步骤:
    1.初始标记。仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际没有额外的停顿。
    2.并发标记。从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户线程并发执行。当对象图扫描完成以后,还要重新处理STAB记录下的在并发时有引用变动的对象。
    3.最终标记:对用户线程进行一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的STAB记录。
    4.筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分的Region的存活对象复制到空的Region中,再清理掉整个Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
    在这里插入图片描述
  • Shenandoah收集器
  • ZGC收集器

8.G1回收器采用了什么样的回收算法?

G1回收器从整体上来看是基于标记-整理算法实现的,从局部上看又是基于标记-复制算法实现的。

8.什么时候会触发Full GC?

在这里插入图片描述

  1. 调用System.gc()。
    只是建议虚拟机执行Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。
  2. 未指定老年代和新生代大小,堆伸缩时会产生Full GC,所以一定要配置-Xmx、-Xms。
  3. 老年代空间不足。
    老年代空间不足常见场景比如大对象、大数组直接进入老年代,长期存活的对象进入老年代等。
    为了避免以上原因引起的Full GC,应当尽量不要创建过大的对象以及数组。
    除此之外,可以通过-Xmn虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。
    还可以通过-XX:MaxTenuringThreshold调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
    在执行Full GC后空间仍然不足,则抛出错误:java.lang.OutOfMemoryError:Java heap spcae
  4. JDK1.7及以前的永生代空间满
    在JDK1.7之前,HotSpot虚拟机中的方法区是用永久代实现的,永久代中存放的一些Class的信息、常量、今天变量等数据。当系统中要加载的类、反射的类和调用的方法比较多时,永久代可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。
    如果Full Gc仍然回收不了,那么虚拟机会抛出java.lang.OutOfMemoryError PermGen space。为避免以上原因引起的Full GC,可采用的方法为增大Perm Gen或转使用CMS GC。
  5. 空间分配担保失败
    1.每次晋升的对象的平均大小>老年代剩余空间
    2.Minor GC后存活的对象超过了老年代剩余空间
    注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当出现这两种状况的时候就有可能会触发Full GC。

promotion failed 是在进行 Minor GC时候,survivor space空间放不下只能晋升老年代,而此时老年代也空间不足时发生的。

concurrent mode failure 是在进行CMS GC过程,此时有对象要放入老年代而空间不足造成的,这种情况下会退化使用Serial Old收集器变成单线程的,此时是相当的慢的。

如何调优:
尽量把对象在新生代使用回收,减少晋升老年代的几率。

9.服务器运行过程中频繁发生Full GC,可能存在哪些问题?如何排查?

检查启动参数
一开始就应该通过pstack来看,full gc是什么地方在触发,然后反推
使用命令 jstat -J-Djstat.showUnsupported=true -snap [pid] 查看平均晋升空间
jstat -gccause pid 查看gc详细原因
常见经验:
1)Java应用的jvm参数Xms与Xmx保持一致,避免因所使用的Java堆内存不够导致频繁full gc以及full gc中因动态调节Java堆大小而耗费延长其周期。
2)建议不要调用System.gc或者Runtime.getRuntime().gc,否则本次调用可能会成为“压死骆驼的最后一根稻草”。当然我们可以通过设置jvm参数禁止这种调用生效,但是除非特别有把握该参数有必要添加,否则不推荐这么设置。
3)对于需要Cache比较多内容的场景而言(尤其是启动时既要加载的),还是要给old留有一定的空间,否则悲观策略就要发威了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值