GC--垃圾回收

本文详细阐述了垃圾回收的概念,包括垃圾的定义、垃圾回收时机、不同区域的回收关注点、垃圾回收算法(如可达性分析、标记-复制、标记-清除等)、对象的finalization机制,以及各种垃圾收集器的分类、工作模式和性能指标。特别关注了内存溢出、内存泄漏和STW机制。
摘要由CSDN通过智能技术生成

目录

垃圾回收概念

什么是垃圾?

垃圾回收机制什么时候会进行GC??

应该关心垃圾回收那些哪些区域的回收

垃圾回收相关算法

垃圾回收算法:[标记阶段、回收阶段]

垃圾标记阶段

标记阶段的目的

引用计数算法(目前没有在使用)

什么是循环依赖问题??

可达性分析算法(当前使用的算法)

什么是根对象??

垃圾回收阶段

标记-复制算法(Copying)

标记-清除算法(Mark-Sweep)

标记-压缩算法(Mark-Compact)

对象的 finalization 机制

finalize()方法 

对象在虚拟机中的三种状态

垃圾回收相关概念

内存溢出与内存泄漏

STW (stop the world)

垃圾回收器

垃圾回收算法是理论,垃圾回收器是回收的实践者

垃圾收集器分类

按线程数分类

        单线程垃圾回收器(Serial)

        多线程垃圾回收器(Parallel)

按工作模式分

        独占式垃圾回收器(STW)

        并发式垃圾回收器

按工作的内存区间分

        年轻代的垃圾回收器

        老年代的垃圾回收器

GC性能指标

CMS回收器(并发标记清除)[追求低停顿]

垃圾回收过程

        初始标记:

        并发标记:

        重新标记:

        并发清除:

G1垃圾回收器     

垃圾回收过程 

 初始标记:

 并发标记:

 最终标记:

 筛选回收:


垃圾回收概念

什么是垃圾?

        垃圾是指在运行程序中没有任何引用指向的对象,这个对象就是垃圾,垃圾对象需要被清理回收,否则会一直被占用,浪费空间,其他新对象无法使用该控件,严重的话会造成内存溢出

垃圾回收机制什么时候会进行GC??

  1. 堆内存不足:当JVM的堆内存不足以分配新的对象时,会触发垃圾回收以释放不再使用的内存空间。
  2. 系统调用:可以通过调用System.gc()Runtime.getRuntime().gc()来建议JVM进行垃圾回收,但具体何时执行是由JVM决定的。
  3. 作用域结束:当对象的作用域结束时,例如方法执行完毕,该作用域内的局部变量将不再被引用,这些对象就可能被回收。
  4. 程序正常退出:当程序执行了System.exit()方法时,JVM在退出前会进行垃圾回收以清理资源。
  5. 对象引用丢失:如果一个对象没有任何引用指向它,那么这个对象就成为垃圾回收的候选对象。
  6. 内存泄漏检测:某些JVM实现可能会定期检查内存使用情况,以识别和处理内存泄漏问题。
  7. 定时回收:一些JVM实现可能会有一个定时机制,定期执行垃圾回收以保持内存的整洁。
  8. 老年代空间不足:当老年代空间不足以容纳新晋升的对象或不足以进行Minor GC时,会触发Full GC。
  9. 元数据区溢出:在Java 8及以后的版本中,如果元空间(Metaspace)不足以存储类的元数据,也可能触发垃圾回收。
  10. 新生代空间不足:当新生代空间不足以分配新对象时,会触发Minor GC。
  11. Survivor空间溢出:如果Survivor空间中的对象太多,无法容纳新的对象,也会触发Minor GC。

应该关心垃圾回收那些哪些区域的回收

重点回收堆{

        频繁回收新生代

        较少回收老年代

}

较少回收方法区

垃圾回收相关算法

垃圾回收算法:[标记阶段、回收阶段]

垃圾标记阶段

标记阶段的目的

标记阶段的目的主要是为了判断对象是否为垃圾对象

        在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是有用对象,哪些是垃圾对象。只有被标记为己经是垃圾对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。 

        那么怎样判定一个对象是否为垃圾对象呢?简单来说:当一个对象没有任何引用指向时,就可以称之为垃圾对象。

引用计数算法(目前没有在使用)

        为每个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况

 对于一个对象A,只要有一个引用指向了对象A,则对象A的引用计数器属性加1;当引用失效时,引用计数器就减1;只要对象A不可能再被使用,则表示可进行回收。

优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性

缺点:

        这样的做法增加了存储空间的开销(占内存)

        每次操作都需要更新计数器,增加了时间开销(费时间)

        不可处理循环依赖问题

什么是循环依赖问题??

 

        当外界有引用指向这个系统时,A的引用会指向B,B的引用指向C,C引用又指向A,这样如果外部请求引用切断时,该系统与外界就失去了联系,但是他们的程序计数器属性不为0,这样会导致循环引用问题,从而导致内存泄漏 

可达性分析算法(当前使用的算法)

可达性分析:也可称之为根搜索算法、追踪性垃圾收集

        相较于引用计数算法,可达性分析算法有效的解决了引用计数算法中的循环引用问题,防止内存泄露问题的发生。

        从一些活跃对象(GC Roots)开始搜索,与跟对象相关联的对象都是被使用的对象,与根对象或者根对象的引用链不相连的,则称之为垃圾对象。

什么是根对象??
  1. 虚拟机栈中引用的对象(正在运行的方法中的对象)
  2. 静态属性(static)
  3. 被用来当做同步锁的
  4. Java系统中的类

垃圾回收阶段

标记-复制算法(Copying)

        可以有多块内存,每次至少有一块是空闲的,复制算法会将存活的对象移动到未被使用的空间中,清除其它块中所有的垃圾对象。

特点:内存碎片少,适用于存活对象少,垃圾对象多的区域(适用于新生代)

标记-清除算法(Mark-Sweep)

        存活对象的位置不变,垃圾对象的地址记录在一个空闲的列表中,如果创建新对象,则将空闲列表中垃圾对象覆盖掉。

特点:不移动对象,回收后会产生内存碎片(适用于老年代)

标记-压缩算法(Mark-Compact)

        标记-清除算法+重新排列

        将存活的对象重新排列,其余空间进行清理,又称:[标记-清除-压缩算法]

 

特点:回收后压缩,不会产生内存碎片(适用于老年代)but效率低

对象的 finalization 机制

        Java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁 之前的自定义处理逻辑(如Servlet生命周期中的destroy()方法)

finalize()方法 

        Object类中,在对象回收之前,可以在此方法中执行一些所需要的逻辑(close()操作等),在对象被判定为垃圾对象时,回收之前会调用finalize()方法,finalize()方法只会被调用一次。

对象在虚拟机中的三种状态

  • 可触及的:从根节点可到达的。
  • 可复活的:被标记为垃圾对象,但还未finalize()的。
  • 不可触及的:finalize()已调用,且被垃圾回收机制执行的。

垃圾回收相关概念

内存溢出与内存泄漏

内存溢出:

        内存溢出Out Of Memory,简称OOM

        经过垃圾回收后,内存仍然不够,导致程序崩溃。

        大多数情况下GC会根据各个年龄段的不同来进行垃圾回收,但是 如果实在回收不了,就会进行 Full GC ,这时候会释放出大量的空间,以供程序应用继续使用。

        存溢出可能导致程序崩溃或系统的不稳定。

内存泄漏:(内存浪费)

        一个对象在程序中不会被使用,但是垃圾回收器不能回收,会一直占用内存,进而导致内存泄漏(e.g:IO,数据库连接等为close()、单例模式中单例对象,整个程序中使用唯一的对象,如果不关闭,回收器就无法进行回收)

STW (stop the world)

        Stop-the-World,简称 STW,当垃圾回收时(标记/回收)会导致其他用户线程暂停,必须保证分析时,其他对象的引用不会出现变化,避免出现漏标、错标等问题,保证分析的准确性。

垃圾回收器

垃圾回收算法是理论,垃圾回收器是回收的实践者

垃圾收集器分类

按线程数分类

        单线程垃圾回收器(Serial)

                适用于一些小的设备,只有一个线程进行垃圾回收

        多线程垃圾回收器(Parallel)

                提供多个线程进行垃圾回收

按工作模式分

        独占式垃圾回收器(STW)

                垃圾回收线程执行时,其他用户线程暂停

        并发式垃圾回收器

                垃圾回收线程可以和用户线程同时执行

按工作的内存区间分

        年轻代的垃圾回收器
        老年代的垃圾回收器

GC性能指标

        吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)

        垃圾收集开销:垃圾收集所用时间与总运行时间的比例。

        暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。

        内存占用:Java 堆区所占的内存大小。

CMS回收器(并发标记清除)[追求低停顿]

        首个实现垃圾收集线程与用户线程可以同时执行的。

        注意:不是所有都并发执行,也会有独占执行。

垃圾回收过程

        初始标记:

                Stop The World,仅使用一条初始标记线程对所有与 GC Roots 直接关联的对象进行标记。

        并发标记:

                垃圾回收线程,与用户线程并发执行。此过程进行可达性分析,标记出所有废弃对象。

        重新标记:

                Stop The World,使用多条标记线程并发执行,将刚才并发标记过程中新出现的废弃对象标记出来。

        并发清除:

                只使用一条 GC 线程,与用户线程并发执行,清除刚才标记的对象。 这个过程非常耗时。

        并发标记与并发清除过程耗时最长,且可以与用户线程一起工作,因此,总体上说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。

G1垃圾回收器     

        适合大型服务器,内存大,CPU更先进,将每个区域(Eden,幸存者,老年代)又划分成若干小的区域,哪个区域垃圾数量多,优先回收哪个区域。可以做到整堆管理收集回收,也可以并发收集回收。

垃圾回收过程 

 初始标记:

                标记出 GC Roots 直接关联的对象,这个阶段速度较快,需要停止用户线程,单线程执行。

 并发标记:

                从 GC Root 开始对堆中的对象进行可达性分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。

 最终标记:

                修正在并发标记阶段引用户程序执行而产生变动的标记记录。

 筛选回收:

                筛选回收阶段会对各个区域的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来指定回收计划(用最少的时间来回收包含垃圾最多的区域)。

这就是 Garbage First 的由来——第一时间清理垃圾最多的区块,这里为了提高回收效率,并没有采用和用户线程并发执行的方式,而是停顿用户线程。

适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值