面试必问的JVM垃圾回收(一)

什么是垃圾回收?

引用的对象称为活动对象,不再被引用的对象将被视为死对象,被称为垃圾。找到并释放(也称为回收)这些对象所使用的空间的过程被称为垃圾收集。垃圾收集解决了许多(但不是全部)内存分配问题。您可以创建对象,无限期地继续引用它们,直到没有更多的内存可用。垃圾收集也是一种

复杂的任务需要自己的时间和资源。用于组织内存、分配和释放空间的精确算法由垃圾处理收集器处理,这一系列过程对开发人员是隐藏的。空间通常是从一个大的内存池中分配的,就像堆一样,垃圾回收的时间由垃圾回收器决定。通常,整个堆或其子部分是当它填满或达到占用率的阈值百分比时收集。完成一个分配请求的任务,它包括在内存中找到一个特定大小的未使用内存块堆,是很难的。大多数动态内存分配算法的主要问题是避免内存碎片化,同时保持分配和解除分配的效率。

如何定位垃圾?

判断对象存活算法:

一.引用计数法

  堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1,但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当该对象实例被垃圾收集时,它引用的所有对象实例的引用计数器减1。但是显而易见,如果有对象循环引用,则此种算法无法将对象的引用数计为0,则对象无法回收,导致内存溢出的风险.

public class quoteTest {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
          
        o1 = o2;
        o2 = o1;
        
        o1 = null;
        o2 = null;
    }
}

  这段代码是用来验证引用计数算法不能检测出循环引用。将o1和o2置空,也就是说o1和o2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数器都不为0,垃圾收集器永远不会回收它们。

二. 可达性分析

  从节点GC ROOTS开始,沿着引用链寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,为可回收的对象。

在Java语言中,可作为GC Roots的对象包括下面几种:

  1) 虚拟机栈中引用的对象(栈帧中的本地变量表);

  2) 方法区中类静态属性引用的对象;

  3) 方法区中常量引用的对象;

  4) 本地方法栈中JNI(Native方法)引用的对象。

关于引用:

  判定对象是否存活都与“引用”有关,在Java语言中,将引用又分为强引用、软引用、弱引用、虚引用4种,这四种引用强度依次逐渐减弱。

  • 强引用

  在程序代码中普遍存在的,类似 Object obj = new Object() 这类引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

  • 软引用

  用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收后还没有足够的内存,才会抛出内存溢出异常。

  • 弱引用

  也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

  • 虚引用

  是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。它的作用是能在这个对象被收集器回收时收到一个系统通知。

对象何时被真正回收呢?

  要真正宣告一个对象死亡,至少要经历两次标记过程。

  第一次标记:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记;

  第二次标记:第一次标记后接着会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,则会直接被回收,如果有必要执行finalize()方法,这个对象将会放置在一个叫做F-Queue的队列之中,由其他xian。在finalize()方法中没有重新与引用链建立关联关系的,将被进行第二次标记。

  第二次标记成功的对象将真的会被回收,如果对象在finalize()方法中重新与引用链建立了关联关系,那么将会逃离本次回收.

垃圾收集器的职责:

•分配内存

•确保任何引用的对象保留在内存中.

•回收内存时不会从执行代码中引用的对象使用内存中恢复

当无法从正在运行的程序中任何其他活动对象的任何引用中访问该对象时,该对象被视为垃圾,并且JVM可以重用其内存

如下图3-1所示

有些对象的寿命更长,因此分布向右延伸。例如,通常在初始化时分配一些对象,这些对象一直存在直到JVM退出。在这两个极端之间的是在某些中间计算过程中存在的对象,在这里将其视为初始峰右边的肿块。一些应用程序的外观分布非常不同,但是令人惊讶的是,大量应用程序具有这种总体形状。通过关注大多数对象“年轻”的事实,可以进行有效的收集。

堆内存分代管理:

jvm在启动时,Java HotSpot VM会在地址空间中保留整个Java堆,但除非需要,否则不会为其分配任何物理内存。从逻辑上讲,覆盖Java堆的整个地址空间都分为年轻一代和老一代。保留给对象存储器的完整地址空间可以分为年轻一代和老一代。

年轻一代由伊甸园和两个幸存者空间组成。大多数对象最初都在eden中分配。一个幸存者空间随时是空的,在伊甸园中用作活动对象的目的地,而在垃圾回收期间,另一个幸存者空间用作目的地;垃圾回收后,伊甸园和源幸存者空间为空。在下一个垃圾回收中,交换两个生存空间的目的。最近填充的一个空间是复制到另一个幸存者空间中的活动对象的来源。以这种方式在幸存者空间之间复制对象,直到将它们复制了一定次数或空间不足为止。这些对象被复制到老年代中。是一个老化过程。

垃圾收集器分类:

串行收集器

串行收集器使用单个线程来执行所有垃圾收集工作,这使之相对高效,因为线程之间没有通信开销。

它最适合单处理器计算机,因为它无法利用多处理器硬件,尽管它在多处理器上对于数据集较小(最大约100 MB)的应用很有用。默认情况下,在某些硬件和操作系统配置上选择了串行收集器,或者可以使用选项明确启用串行收集器-XX:+UseSerialGC。

并行收集器

并行收集器也称为吞吐量收集器,它是类似于串行收集器的分代收集器。串行收集器和并行收集器之间的主要区别是并行收集器具有多个线程,这些线程用于加速垃圾收集。

并行收集器旨在用于具有在多处理器或多线程硬件上运行的中型到大型数据集的应用程序。您可以使用-XX:+UseParallelGC选项启用它。

并行压缩是使并行收集器能够并行执行主要收集的功能。如果没有并行压缩,则使用单个线程执行主要集合,这会大大限制可伸缩性。如果-XX:+UseParallelGC已指定选项,则默认情况下启用并行压缩。您可以使用 -XX:-UseParallelOldGC 选项禁用它。

垃圾优先(G1)垃圾收集器

G1是大多数并发收集器。通常,并发收集器会与应用程序同时执行一些昂贵的工作。该收集器旨在从小型计算机扩展到具有大量内存的大型多处理器计算机。它提供了以高概率满足暂停时间目标的能力,同时实现了高吞吐量。

默认情况下,G1在大多数硬件和操作系统配置中处于选中状态,或者可以使用明确启用-XX:+UseG1GC。

Z垃圾收集器

Z垃圾收集器(ZGC)是可伸缩的低延迟垃圾收集器。ZGC同时执行所有昂贵的工作,而不会停止执行应用程序线程。

ZGC可以提供几毫秒的最大暂停时间,但是会牺牲一些吞吐量。它适用于要求低延迟的应用程序。暂停时间与正在使用的堆大小无关。ZGC支持从8MB到16TB的堆大小。要启用此功能,请使用该 -XX:+UseZGC选项。

选择收集器

除非您的应用程序有非常严格的暂停时间要求,否则可以选择其他收集器。

如有必要,请调整堆大小以提高性能。如果性能仍然不能满足您的目标,请使用以下准则作为选择收集器的起点:

  • 如果应用程序的数据集较小(最大约为100 MB),则选择带有选项的串行收集器-XX:+UseSerialGC。
  • 如果应用程序将在单个处理器上运行,并且没有暂停时间要求,则选择带有选项的串行收集器-XX:+UseSerialGC。
  • 如果应用程序性能是第一要务,并且没有暂停时间要求或一秒或更长时间的暂停是可接受的,则让VM选择收集器或使用选择并行收集器-XX:+UseParallelGC。
  • 如果响应时间比整体吞吐量更重要,并且必须将垃圾收集暂停时间保持更短,那么请使用选择并发的收集器-XX:+UseG1GC。
  • 如果响应时间是高优先级,请使用选择一个完全并发的收集器 -XX:UseZGC。

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

如果推荐的收集器没有达到所需的性能,则首先尝试调整堆和分代大小以满足所需的目标。如果性能仍然不足,请尝试使用其他收集器:使用并发收集器来减少暂停时间,并使用并行收集器来增加多处理器硬件上的总体吞吐量。

 

关注我的公众号 :  宇哥996(id: java_zyh)   Java全栈技术,大厂面试题不定期分享

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值