Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java后端面试学习知识总结——GC

Java后端面试学习知识总结——GC

作者头像
星如月勿忘初心
发布于 2020-08-01 07:46:16
发布于 2020-08-01 07:46:16
4060
举报
文章被收录于专栏:星月小站星月小站

垃圾回收算法

JVM之所以能够自动回收内存,是因为JVM的开发人员使用了一些垃圾回收算法,来让JVM自己判断哪些对象可以回收,哪些对象不可以回收。

实际上垃圾收集并不是Java语言或者JVM专属的,垃圾回收算法主要分为两个阶段的算法,分别是标记算法回收算法

标记算法一般用来判断对象是否为垃圾,是否可以回收。

回收算法一般用来回收被标记为垃圾的对象。

二者一般结合使用。

垃圾回收之标记算法

  • 对象被判定为垃圾的标准:没有被其他对象引用。
  • 判定对象是否为垃圾的算法主要有两种:引用计数算法可达性分析算法
  • 关于引用计数法,有一个误会,这个算法虽然效率高,比较简单,但是主流的Java虚拟机并没有采用这个算法,因为需要额外考虑的情况太多,维护成本高。读者可以自行创建两个互相引用对方属性的对象,然后将两个对象置空,会发现对象依然可以被GC回收。下面虽然会介绍引用计数法,但读者需要了解,这个算法并没有被JVM采用。
  • 引用计数算法(Java虚拟机并未采用)
    • 通过判断对象的引用数量来决定对象是否可以被回收。
    • 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1。
    • 任何引用计数为0的对象实例都可以被当做垃圾收集。
    • 优点:执行效率高,程序执行受垃圾回收影响较小。
    • 缺点:无法检测出循环引用的情况,如果像个对象指向互相引用,会造成无法回收,内存泄漏。
  • 可达性分析算法(Java虚拟机的主流标记算法)
    • 通过判断GCRoots根对象的引用链是否可达来决定对象是否可以被回收,如下图所示:
  • Java技术体系中,可以被当做GC Root的对象主要如下:
    • 虚拟机栈中引用的对象(栈帧中的本地变量表)。
    • 方法区中的常量引用的对象。
    • 方法区中类静态属性引用的对象。
    • 本地方法栈中JNI(Native方法)的引用对象。
    • 所有被同步锁(synchronized关键字)持有的对象。

垃圾回收之回收算法

  • 分代收集理论:在介绍垃圾收集算法之前,首先要先说明一下分代收集理论。因为目前的商业虚拟机中的垃圾收集器,大多数都遵循了“分代收集”理论来设计,有时也会被称为分代回收算法/分代收集算法。分代收集虽说是理论,其实是一种经验假说,其建立在两种假说之上:
    • 弱分代假说:绝大多数对象都是朝生夕灭的。
    • 强分代假说:熬过越多次垃圾收集的对象就越难以灭亡。

    根据这两种假说,现代的商业Java虚拟机一般至少将堆内存分成两个部分——新生代老年代两个区域。顾名思义,新生代的对象一般都是刚刚创建的,根据弱分代假说,大多数对象会在短时间内被新生代的垃圾收集器回收掉,而熬过了多次回收的对象,则会进入老年代。 于是Java的垃圾收集就可以每次只针对其中某个区域进行回收,提高回收效率,因而才有了"Minor GC""Major GC""Full GC"等回收类型的划分。也针对不同区域的对象特征发展除了不同的垃圾收集算法——"标记-清除算法""标记-复制算法""标记-整理算法"等针对性的垃圾收集算法。下面将会具体展开介绍,但是读者需要明白,这一切都始于分代收集理论。

  • 标记-清除算法
    • 标记-清除算法是出现最早也是最基础的垃圾收集算法,如同其名字一样,算法首先标记出所有需要回收的对象,然后统一回收掉所有被标记的对象,也可以反过来标记存活的对象,统一回收未被标记的对象。
    • 标记:从跟集合进行扫描,对存活的对象进行标记。
    • 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存。
    • 缺点:1.对象越多执行效率越低。2.标记清除算法会产生内存碎片。
  • 标记-复制算法
    • 标记复制算法将可用内存空间分成对象面空闲面,每次只使用对象面创建对象,当对象面内存用完了,就将对象面中存活的对象复制到控线面中,然后清空对象面,此时空闲面就变成了对象面,对象面就变成了空闲面。
    • 标记-复制算法解决了内存碎片化的问题,而且是顺序分配内存,简单高效。
    • 缺点:这种算法将可用内存减少了一半,空间比较浪费,如果对象存活率高的话就会发生频繁GC的情况,所以适用于对象存活率低的场景。也就是说,不适用于老年代,一般应用于新生代。
  • 标记-整理算法
    • 标记整理算法有些像标记清除算法,但是标记整理算法是移动式回收算法。标记整理算法也是分为两步,先标记存活的对象,然后对存活对象进行整理,移动所有存活对象按照内存地址从内存头部依次排列,最后将末端存活对象以后的内存全部回收。
    • 由于标记整理算法需要移动对象,特别是老年代这种每次回收都有大量存活对象的区域,移动存活对象并更新其引用地址就是一种极为负重的操作,这种更新过程必须暂停全部用户应用程序才能进行,这种停顿也有一个命名叫做"Stop The World"
    • 既然标记整理算法代价这么大,为什么还要用这种方式呢,这是权衡需求与利弊的产物。标记清除算法虽然有可能不需要频繁地停顿用户进程,但是其会产生内存碎片,虚拟机就需要维护一个“分区空闲分配链表”来对空闲内存进行记录,这也影响了系统的效率。根据计算,发现标记清除算法延迟低但是吞吐量低标记整理算法虽然延迟高,但是吞吐量大。根据不同的需求,就可以选择不同的回收算法。
    • 当然也有和稀泥式的解决办法,就是让虚拟机平时大多数时间使用标记清除算法,暂时容忍内存碎片,直到碎片比较严重,影响了对象的内存分配,此时就采用标记整理算法进行一次GC来规整内存空间。后文将要介绍的CMS收集器面对碎片过多时,就采用的这种解决办法。

垃圾回收算法的组合拳:分代收集理论/分代收集算法

  • 分代收集算法,其实就是按照前文所介绍的分代收集理论,将不同的基础收集算法应用到不同分代区域中的组合算法。是垃圾回收算法的组合拳,因地制宜的体现。
  • 主流的JVM中,是按照对象生命周期的不同,依据弱分代假说和强分代假说将内存划分为新生代和老年代区域,这种划分的目的是为了提高JVM的回收效率。本文以HotSpot这一最主流虚拟机为例来讲解其分代收集算法的实现。其堆空间划分如下图所示:
  • Minor GCMajor GCFull GC:前文已经说明分代收集理论是为了分区域针对不同对象特点来进行内存回收,于是就有了这三个概念,分别对应年轻代GC、老年代GC和整堆GC。这里提前明确概念以便后文阅读。
    • Minor GC:年轻代GC,指目标只是新生代的垃圾收集活动。
    • Major GC:老年代GC,指目标只是老年代的垃圾收集活动,目前只有CMS收集器会有这种行为。
    • Full GC:整堆收集,指手机整个Java堆和方法区的垃圾收集活动。
  • 新生代/年轻代
    • 划分出新生代区域,是为了尽可能快速地收集掉那些生命周期短的对象。在HotSpot虚拟机中,新生代又划分为一个Eden区和两个Survivor区。两个Survivor区分别以fromto命名,用来进行基于复制算法的垃圾回收,代表了复制算法中的空闲面和对象面。Eden区和两个Survivor区的内存比例是8:1:1,其内存结构如下图所示:
  • HotSpot虚拟机中,大多数情况下,对象在新生代Eden区中分配(特别大的对象可能直接放入老年代),当Eden区没有足够空间进行分配的时候,就会触发一次Minor GC,来释放新生代的空间。垃圾回收工作时,会将Eden区和一个Survivor区中存活的对象放进另外一个Survivor区,然后清除Eden和第一个Survivor区。当一个对象经历了足够多的Minor GC依然存活的话(默认是15次),就会被放入老年代。
  • 这个垃圾回收步骤和标记复制算法的步骤是一致的,其回收流程如下图:
  • 在新生代GC中,对象有两种可能会晋升到老年代中:
    • 经历一定次数的Minor GC依然存活(默认15次)。
    • 在GC过程中,Eden区存活对象尝试复制到Survivor区,但Survivor区放不下的对象会通过分配担保机制提前转移到老年代去。
  • 老年代
    • 老年代中存放的对象一般都是生命周期较长的对象,还有一部分特别大的对象,因新生代放不下而提前转移至老年代。
    • 由于老年代对象存活率高,对象大,所以一般不使用标记复制算法(过于浪费空间),一般使用标记清除算法和标记整理算法(实际上只有CMS才有只针对老年代的GC)。
  • 各种GC的触发条件:
    • Minor GC:新生代的GC主要发生在Eden区内存满的时候。
    • Major GC:Major GC主要发生在CMS垃圾收集器中,当老年代满的时候会先尝试进行Major GC。
    • Full GC:
      • 非CMS收集器,当老年代满的时候会触发Full GC。
      • 当CMS收集器尝试进行GC时发现有对象Eden区放不下,老年代也放不下,或者有对象想直接进入老年代,但老年代空间不足,此时会进行Full GC。
      • Minor GC之后晋升到老年代的对象平均大小大于老年代剩余的空间,就会触发Full GC。
      • 调用System.gc();函数,可以直接触发Full GC。
  • 常见调优参数:
    • -XX:SurvivorRation : Eden和Survivor的比值 (默认8:1)
    • -XX:NewRation : 老年代和年轻代的内存比例
    • -XX:MaxTenuringThreshold : 对象从年轻代晋升到老年代经过的GC最大次数。

经典的垃圾收集器

  • 前置知识:
    • StopTheWorld:JVM由于要执行GC而暂停用户进程。任何GC算法中都会发生。多数GC优化通过减少StopTheWorld发生的时间来提高程序的性能。
    • SafePoint:分析过程中对象引用关系不会发生变化的点,如方法调用,循环跳转,异常跳转等。GC一般就发生在安全点。
    • JVM的运行模式:JVM分为ServerClient两种模式,前者是重量级JVM,启动慢,但稳定后性能高;后者是轻量级JVM,启动速度快,但长时间运行没有Server模式性能高。
      • Service模式下默认的年轻代收集器是Parallel Scabenge收集器
      • Client模式下默认的年轻代收集器是Serial收集器。
    • 吞吐量:运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
  • HotSpot虚拟机中经典的垃圾收集器 这里介绍的经典收集器是在实践中千锤百炼已经成熟的经典收集器,虽然技术上不是最先进,但是可以在商用生产环境上放心使用的收集器。各经典收集器之间的关系如下图所示:

其中,连线表示两个收集器之间可以组合使用,而一些组合(Serial+CMS和ParNew+Parallel Old)在JDK8中被声明废弃,在JDK9中被取消支持。

年轻代的垃圾收集器:

  • Serial垃圾收集器:
    • Serial垃圾收集器是最基础、历史最悠久的垃圾收集器,是一种单线程的垃圾收集器,采用标记-复制算法进行垃圾收集工作的时候必须暂停所有工作线程,直到收集结束。是Client模式下的JVM默认年轻代垃圾收集器。其垃圾收集逻辑如下图:
    • 优点:简单高效,额外内存消耗最小,单/少核心处理器上运行效率高。
    • 缺点:StopTheWorld时间较长。
  • ParNew收集器:
    • ParNew收集器是Serial收集器的多线程版本,除了是多线程并发进行垃圾回收外,别的行为和Serial收集器完全一样。ParNew收集器的工作过程如下所示:
    • ParNew收集器在单核心处理器的环境下性能绝不会比Serial收集器更好,但是随着可以被使用的处理器核心数增多,ParNew收集器就会越来越高效。
  • Parallel Scavenge收集器:
    • Parallel Scavenge收集器也是一款新生代收集器,是Server模式下JVM默认的新生代收集器,而且是使用了标记-复制算法的多线程的收集器。这样看来与ParNew收集器似乎没有什么不同,实际上Parallel Scavenge收集器的特点是该收集器的性能关注点与其他收集器不一样。
    • 其他收集器的优化目标是放在尽可能缩短垃圾收集期间的用户线程停顿时间,Parallel Scavenge收集器的目标则是更关注系统的吞吐量,使其尽可能达到一个可控制的值。
    • 缩短用户线程停顿时间可以提高用户的使用体验,所以这种优化一般使用在需要与用户交互的架构中,而高吞吐量则可以更高效地利用处理器资源来尽快完成运算任务,主要用于后台运算等不需要太多交互的分析任务中。
    • Parallel Scavenge收集器独有的参数控制:
      • -XX:UseAdaptiveSizePolicy(用来把内存管理调优任务交给JVM自己完成)
      • -XX:MaxGCPauseMillis(用来设置最大垃圾收集停顿时间)
      • -XX:GCTimeRatio(用来直接精确设置吞吐量大小)

老年代的垃圾收集器:

  • Serial Old收集器:
    • Serial收集器的老年代版本,也是单线程收集,进行垃圾收集时,必须暂停所有工作线程。
    • 使用标记-整理算法,这个收集器的主要意义是在Client模式下是使用,是Client模式默认的老年代收集器。如果是在Server模式下,当CMS收集器失败时,Serial Old收集器将作为后备预案。
  • Parallel Old收集器:
    • Parallel Old收集器是Parallel Scavenge收集器的老年代版本,多线程并发收集,基于标记-整理算法实现,也是主要关注系统的吞吐量。
    • Parallel Old经常与Parallel Scavenge一起使用来保证系统的吞吐量。
  • 目标最短回收停顿时间——CMS收集器:
    • CMS收集器是一种以获取最短回收停顿时间为目标的收集器,全称为Concurrent Mark Sweep,从名字可以看出,该收集器基于标记-清除算法,一些官方文档中也称之为并发低停顿收集器。目前主要应用于B/S系统的服务端上,提高用户交互体验。
    • CMS收集器虽然基于标记-清除算法,但其运作过程相较于前面几种收集器来说要复杂一些,主要分为四个步骤:
      • 初始标记(需要StopTheWorld):单线程标记一下GC Roots能够直接关联到的对象,速度很快,但是需要暂停用户线程。
      • 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程,此过程耗时较长,但是无需停顿用户线程。
      • 重新标记(需要StopTheWorld):该阶段是为了修正在并发标记期间因用户线程运行而导致标记产生变动的那一部分对象标记记录,重新标记会导致用户线程停顿,比初始标记停顿要长,总体耗时远远小于并发标记阶段。
      • 并发清除:清理删除标记阶段判断为已死亡的对象,由于是标记清除算法,不需要移动对象,所以该阶段也无需停顿用户线程。
      • CMS收集器最耗时的是并发标记和并发清除阶段,这两个阶段是和用户线程一起运行的,所以总体来看,CMS收集器可以认为是和用户线程并发执行的,停顿时间非常短。其运行流程如下图所示:
    • CMS收集器的缺点
      • 对处理器资源非常敏感,核心数较低的处理器使用CMS停顿感可能较强。
      • 无法处理“浮动垃圾”,浮动垃圾是指在并发标记和并发清理阶段,新的垃圾对象还是会一直出现,这一部分只能下一次垃圾收集时再处理,容易引发Full GC降低性能。
      • CMS采用标记清理算法,会产生内存碎片,如果没有足够的连续内存用来存放大对象,就会触发Full GC。
  • G1收集器(Garbage First):
    • G1收集器是一款主要面向服务端应用的垃圾收集器,被开发出来是为了替代CMS收集器,是一种覆盖了新生代和老年代的“全功能垃圾收集器”。JDK9后宣布取代Parallel Scavenge + CMS的垃圾收集器组合,成为了Server模式下默认的垃圾收集器。
    • G1与CMS有许多相似的地方,也是并发和并行一体的垃圾收集器,也是分代收集器,但相对于CMS,G1有了很多的新特点:
      • 可预测的停顿:G1收集器允许用户设定允许的收集停顿期望值(-XX:MaxGCPauseMillis),G1收集器会在期望值以内尽可能收集多的对象。
      • 基于Region的堆内存模型:G1收集器能够面向堆内存任何部分来组成回收集,而不是单独面向新生代或者老年代,因为G1收集器中虽然依然遵循分代理论,但是G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域“Region”。每一个Region都可以根据需求扮演新生代的Eden空间、Survivor空间和老年代空间。收集器根据每个Region的属性来针对性进行回收。其模型图如下:

先进的低延迟垃圾收集器

  • ZGC
    • ZGC全称Z Garbage Collector,是一款在JDK11中加入的具有实验性质的低延迟垃圾收集器,其最初的设计目标是:在尽可能对吞吐量影响不大的情况下,实现在任意堆内存大小下都可以把垃圾回收的停顿时间限制在十毫秒以内的低延迟。
    • ZGC也是基于Region堆内存模型,但是ZGC不设分代,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法,以低延迟为首要目标。
    • ZGC的Region模型与G1有一些区别,ZGC的Region具有动态性——动态创建与销毁以及动态的分配区域容量大小。
      • ZGCRegion分成三种区域类型容量:小型Region:容量固定为2MB,用于放置小于256KB的小对象。
      • 中型Region:容量固定为32MB,用于放置大于等于256KB,但是小于4MB的对象。
      • 大型Region:容量不固定,可以动态变化,但是必须是2MB的整数倍。用于放置大于等于4MB的对象。每个大型Region中只会放置一个大对象。

Java的四大引用类型

在Java的GC机制中,判断对象是否为垃圾,主要依靠的还是对象间的引用关系,为了更好地控制对象之间的引用关系强弱程度,所以在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)软引用(Soft Reference)弱引用(Weak Reference)虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱。

Java中的一切都被视为对象,但是我们在编程时直接操作的却是变量,它实际上是对象的一个引用(reference),通过这个引用标识符来指向内存中的某块区域上存储的对象,就可以利用引用来操作对象了。

  • 强引用(Strong Reference):强引用是Java中默认的声明引用强度,当我们使用new关键字来创建对象的时候,变量和对象间就是强引用的关系,如Object o = new Object();这段代码就会在内存中创建一组强引用关系,内存结构如下图所示:
  • 强引用只要存在,就不会被垃圾回收器回收掉,哪怕内存不足的时候,会直接抛出OOM异常,也不会去回收。
  • 想中断强引用和对象之前的引用关系,可以将引用赋值为null即可,如o = null,此时引用和对象就会断开联系,对象在下次GC就会被回收掉。
  • 软引用(Soft Reference):软引用是用来描述一些非必须但仍有用的对象。
    • 软引用需要显示调用,一个变量只能通过强引用指向堆内存中,所以需要使用java.lang.ref.SoftReference类来中继,表示软引用,演示语句如下:

    SoftReference<Object> softRf = new SoftReference<>(new Object()); 在内存中的结构如下图所示:

  • 内存足够的时候,软引用对象不会被回收,只有在内存不足的时候,系统才会回收软引用对象,如果回收了软引用对象之后,依然没有足够的内存,才会抛出OOM异常。
  • 这种特性常常被用来做缓存技术,比如网页或者图片缓存等,服务器内存压力不大图片就一直放在内存中,用户读取体验很好,当服务器内存压力大的时候,图片缓存就被清理掉,将内存空间尽可能给核心业务使用。
  • 当一个对象唯一剩下的引用是软引用,那么该对象才是软可及的。想要拿到被软引用的那个对象,可以使用java.lang.ref.SoftReference提供的get()方法,比如softRfObj.get();
  • 弱引用(Weak Reference):弱引用的引用强度比软引用更弱。
    • 弱引用也需要显示调用,使用java.lang.ref.WeakReference类来中继,表示弱引用,演示语句如下:

    WeakReference<Object> weakRfObj = new WeakReference<>(new Object()); 在内存中的结构如下图:

  • 弱引用中,无论内存是否足够,只要JVM开始进行垃圾回收,被弱引用关联的对象都会被回收。
  • 弱引用看似没有作用,因为会直接被垃圾回收。但其实弱引用在ThreadLocal中被使用到,而且是降低了ThreadLocal内存泄漏风险的主要方式,具体分析见:ThreadLocal与弱引用
  • 虚引用(Phantom Reference):虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有引用一样,随时可能被回收。
    • 虚引用也需要显示调用,使用java.lang.ref.PhantomReference类来中继,表示虚引用。PhantomReference类源码中只有一个构造函数和一个get()方法,而且get()方法仅仅返回一个空,也就是说永远无法通过虚引用直接获取对象,虚引用必须和ReferenceQueue引用队列一起使用才可以。演示语句如下:

    ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRfObj = new PhantomReference<>(new Object(), queue);

    • 虚引用虽然无法直接获取,但是当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。其运作机制如下图:
    • 所以,当被虚引用关联的对象被回收时,我们可以通过引用队列得知这一情况的发生,然后可以定制化地对这个对象进行回收前的处理。
    • 最经典的应用:管理直接内存
      • JDK的NIO(New Input/OutPut)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,可以通过Native函数库直接分配堆外内存,然后通过Java堆中的DirectByteBuffer对象来对这块内存的引用进行操作,避免数据在Java堆与Native堆中数据的来回复制。也就是说通过一种特殊的指针将JVM中的对象与JVM外的内存连接了起来。
      • 由于JVM的垃圾回收器只能管理与回收JVM内存中的对象,那么如何管理堆外内存中的数据呢?其实就是用过虚引用,将DirectByteBuffer对象关联起来:DirectByteBuffer实例化的会创建一个Cleaner实例,Cleaner是PhantomReference的子类。
      • DirectByteBuffer对象本身其实是很小的,但是它后面可能关联了一个非常大的堆外内存,如果GC回收了DirectByteBuffer对象本身,说明DirectByteBuffer对象所指向的堆外内存也不需要了。Cleaner实例就会被加入到ReferenceQueue中,然后用户线程就可以从队列中收到这个通知,针对性地通过Native的操作来释放堆外内存。
  • 四大引用类型总结

垃圾回收常见面试题与真题

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/06/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java底层知识JVM、GC
答:Java虚拟机,最值的学习的两点,JVM内存结构模型以及GC。JVM是一个内存中的虚拟机,JVM的存储就是内存,例如类、常量、变量、方法都是在内存中。Java虚拟机是一种抽象化的虚拟机,在实际的计算机上仿真模拟各种计算机功能来实现,JVM有自己完善的硬件架构,如处理器,堆栈,寄存器等等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码即字节码,就可以在多种平台上不加修改的运行,一次编译到处运行。
别先生
2020/04/08
6680
Java底层知识JVM、GC
Java核心知识点整理大全2-笔记
为了解决 Mark-Sweep 算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小 的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用 的内存清掉,如图:
用户10920956
2024/01/18
1360
Java核心知识点整理大全2-笔记
jvm之GC知识点
一、GCRoots: 虚拟机栈(栈帧中的局部变量表)引用的对象 方法区中静态属性引用的对象 方法去中常量引用的对象 本地方法栈中JNI(NATIVE方法)引用的对象 二、引用: reference类型数据中存储着另外一块地址的起始地址 强引用:通常的引用,只要引用存在便不会被回收 软引用:有用但非必需的对象,在内存将要发生内存溢出异常时,会将这些对象列入范围,进行二次回收。 弱引用:非必需对象,只能生存到下次回收之前,无论内存充足与否,都会被回收。 虚引用:对生存
WindWant
2020/09/11
2850
面试必问,万字精美图文带你彻底掌握JVM垃圾回收
来源|https://segmentfault.com/a/1190000021516052
用户4172423
2020/02/26
3790
JVM 垃圾回收机制(GC)总结
说起垃圾收集(Garbage Collection),大多数人都会想起Java,这项技术从始至终伴随着Java的成长,但事实上GC的出现要早于Java,它诞生于1960年MIT的使用动态分配和垃圾回收技术的语言Lisp。经过近60年的发展,目前内存的动态分配和内存回收技术已经非常成熟了,所有的垃圾回收已经自动化,经过迭代更新,自动回收也经过反复优化,效率和性能都非常可观。
beifengtz
2019/06/03
4.4K0
JVM 垃圾回收机制(GC)总结
java虚拟机笔记(二)
上一篇文章我们了解了jvm的内存分配,在这篇文章我们将讲一讲Java虚拟机的垃圾回收。
小森啦啦啦
2019/07/13
3510
Java底层:GC相关
光有垃圾标记算法还不行,JVM还需要有垃圾回收算法来将这些标记为垃圾的对象给释放回收掉。主要的回收算法有以下几种:
端碗吹水
2020/09/23
6000
Java底层:GC相关
老生常谈Java虚拟机垃圾回收机制(必看篇)
程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。
李红
2019/05/29
3680
java8 各种GC的总结
标记-清除算法分为标记和清除两个阶段:首先标记出需要回收的对象,在标记完成后统一回收所有被标记的对象。 存在的问题: 一是效率低,标记和清除两个过程效率都不高。二是空间问题,标记清除后会产生大量的不连续的内存碎片。空间碎片太多会导致程序在运行过程中需要分配较大对象时无法找到连续内存而不得不提前触发GC。
冬天里的懒猫
2021/08/13
1K0
java8 各种GC的总结
JVM 学习笔记(2):垃圾回收GC
这个算法原理简单,判断效率也高,但如果出现对象之间的相互引用(循环引用),那么相应对象的引用计数器将永不为0,也就无法被回收掉,即使他们已经没有存活的意义。
玛卡bug卡
2022/09/20
2570
JVM 学习笔记(2):垃圾回收GC
Java面试集锦(一)之Jvm(虚拟机)
 Stop-the-World:   JVM由于要执行GC而停止了应用程序的执行称之为Stop-the-World,该情形会在任何一种GC算法中发生。当Stop-the-world发生时,除了GC所需的线程以外,所有线程都处于等待状态直到GC任务完成。事实上,GC优化很多时候就是指减少Stop-the-world发生的时间,从而使系统具有 高吞吐 、低停顿 的特点。
凯哥Java
2022/12/16
2770
Java面试集锦(一)之Jvm(虚拟机)
Java面试高频知识点汇总 垃圾回收(GC)机制专题
上图所示的eden区、s0区、s1区都属于新生代,tentired区属于老年代。大部分情况,对象都首先在eden区分配,在一次新生代垃圾回收之后,如果对象依然存活,则会进入s0或者s1,并且对象年龄还会加1(Eden区->Survivor区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代,这个阈值可以通过参数-XX:MaxTenuringThreshold来设置。
Steve Wang
2020/09/24
5760
图解Java 垃圾回收机制
  Java技术体系中所提倡的 自动内存管理 最终可以归结为自动化地解决了两个问题:给对象分配内存 以及 回收分配给对象的内存,而且这两个问题针对的内存区域就是Java内存模型中的 堆区。关于对象分配内存问题,笔者的博文《JVM 内存模型概述》已经阐述了 如何划分可用空间及其涉及到的线程安全问题,本文将结合垃圾回收策略进一步给出内存分配规则。垃圾回收机制的引入可以有效的防止内存泄露、保证内存的有效使用,也大大解放了Java程序员的双手,使得他们在编写程序的时候不再需要考虑内存管理。本文着重介绍了判断一个对象是否可以被回收的两种经典算法,并详述了四种典型的垃圾回收算法的基本思想及其直接应用——垃圾收集器,最后结合内存回收策略介绍了内存分配规则。
Java高级架构
2018/09/29
6670
图解Java 垃圾回收机制
面试官:你对JVM垃圾收集器了解吗?13连问你是否抗的住!
4、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
程序员追风
2020/05/15
2.8K0
面试官:你对JVM垃圾收集器了解吗?13连问你是否抗的住!
java(9)-深入浅出GC垃圾回收机制
1、本文了解GC垃圾回收机制,深入理解GC后才明白,为啥FGC会导致stop-the-world。 2、了解GC算法。
黄规速
2022/04/14
1.1K0
java(9)-深入浅出GC垃圾回收机制
JAVA垃圾回收机制
PS:不管是调优也好,先是配合对应的新老对应的垃圾收集器,如果有必要才修改垃圾收集器的参数的,垃圾回收器如何标记垃圾,如何进行垃圾收集的,收集器有哪些?它们是如何配合的。
IT架构圈
2021/02/01
6020
JAVA垃圾回收机制
搞定JVM垃圾回收就是这么简单
当需要排查各种 内存溢出问题、当垃圾收集称为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。
用户2164320
2018/08/29
6310
【进击面试_04】Java 虚拟机
JVM 是 Java 虚拟机 Java Virtual Machine 的缩写,JVM 是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。引入 Java 语言虚拟机后,Java 语言在不同平台上运行时不需要重新编译。Java 语言使用 Java 虚拟机屏蔽了与具体平台相关的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
Demo_Null
2021/03/15
4020
JVM垃圾回收(GC)
对象被引用一次,在它的对象头上加一次引用次数,如果没有被引用(引用次数为 0),则此对象可回收
chenchenchen
2022/01/05
3590
JVM垃圾回收(GC)
JVM垃圾回收机制
Java 内存运行时区域中的程序计数器、虚拟机栈、本地方法栈随线程而生灭,栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会由 JIT 编译器进行一些优化),因此这几个区域的内存分配和回收都具备确定性,不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。 而 Java 堆不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存。
CodingDiray
2019/10/14
4810
JVM垃圾回收机制
相关推荐
Java底层知识JVM、GC
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档