(入门学习) 垃圾回收(GC)、垃圾清除算法以及各种垃圾回收器

本文深入探讨了内存的申请与释放,对比了C/C++与Java的内存管理方式。Java通过垃圾回收机制避免内存泄漏,介绍了引用计数和可达性分析两种垃圾检测方法。同时,详细阐述了不同垃圾回收算法,如标记-清除、复制-清除和标记-压缩,以及JVM的分代收集策略。此外,还讨论了各种垃圾回收器,如Serial、ParallelScavenge和CMS,以及它们在不同场景下的应用。最后,提到了JVM命令行参数对垃圾回收的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内存的申请和释放

我们在编程的过程中,经常需要为了存放数据而申请内存,当数据使用结束之后需要释放内存,各种高级编程语言都提供了申请和释放内存的方法:

  • c: new - free
  • c++:new-delete
  • java:new-垃圾自动回收

对于c/c++来说,需要手动申请和释放内存,坏处是:一、忘记释放内存,生成过多内存垃圾,导致内存泄漏,二、重复释放内存,程序运行中报错。 因此,在java中,使用垃圾自动回收器自动回收内存,可以减少编程中出现的错误。怎么去设计一个垃圾回收器,首先要考虑以下问题。

什么是垃圾?

内存垃圾是指在内存中没有任何引用指向的对象。

如何找到垃圾?

一、引用计数法。

当一个对象被一个引用指向之后,他的引用计数+1,当一个对象的引用计数变为0,说明没有引用指向它,它在内存中是不可被找到的,可以视为内存垃圾,可以回收。缺点是,当内存中出现几个对象循环指向,每个对象的引用计数都为1,但是,这些对象都是不可达的,都属于内存垃圾。

二、可达性分析。

JVM将某些对象视为根对象,根对象中的成员变量会指向其他的对象,因此,层层指向会形成一颗对象树,树中的对象被认为是可以达的,树外的对象视为不可达。当JVM遇到不可达对象时,会将对象进行一次标记,如果标记后第二次任然不可达,则对象被当成垃圾清除。

关于为什么需要进行2次标记?

某些对象中会有一个finalize()方法,这个finalize函数的意义同finalize关键字意义类似。如果JVM发现对象实现了finalize()函数,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环,将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

什么样的对象可以作为根对象?

  1. 虚拟机栈中的引用对象(方法的本地变量)
  2. 方法区中的类静态属性引用的对象
  3. 方法区中的常量引用的对象(声明为final的常量值)
  4. 本地方法栈中JNI的引用对象(方法的本地变量)

怎么清理垃圾?

一、标记-清除算法 mark-sweep

将内存中的垃圾对象直接标记然后清除,这样缺点是会导致过多的内存碎片,降低系统的效率。

二、复制-清除算法 copy-sweep

这种算法将内存一分为二,每次使用内存的一半,当一半内存满了之后,进行一次标记,将存活的对象复制到另一半内存,对原来的一半内存直接清除。这种算法不会产生内存碎片,效率高,但是每次只使用一半内存,所以内存的利用率只有50%,利用率低。

三、标记-压缩算法 mark-compact

这种算法进行一次标记之后,将所有存活的对象移动到内存的一边,剩下的区域直接清理。这种算法利用率高、不产生碎片,但是需要频繁移动存活对象,所以效率低。

JVM分代算法

堆逻辑分区

在这里插入图片描述

JVM根据内存的活跃度将堆内存划分为年轻代(young)和年老代(old),内存大小的比为1:3。在年轻代中,堆内存被划分为Eden、survivor1和survivor2区,内存大小为8:1:1。(内存大小与比例可以设置)

当Java创建一个对象时,首先会在Eden中分配一块内存。如果整个Eden区都装不下,则直接在年老代中创建对象。如果Eden能能装下但是空闲内存不够,则会进行一次Minor GC\Young GC\YGC。在年轻代中使用的是复制-清除算法(因为80%的对象是需要清除的,能持续很久的对象很少,所以只需要找出少量的存活对象然后复制,节省时间,这也是Eden:s1:s2=8:1:1的原因),清理过程如下:

  1. 标记Eden和s1(survivor1)中的对象,将存活对象移动到s2(survivor2),清除Eden和s1中的所有对象
  2. 下一次回收时,标记Eden和s2中的对象,将存活对象移动到s1,清除Eden和s2中的所有对象
  3. 重复1、2步骤
  4. 当一个对象的年龄足够(>15,cms>16)时,直接将对象移动到老年代。

当老年区内存不够时,触发Major GC\Full GC\FGC,Major GC采用的是标记整理法,对这个内存进行标记整理,当整理时程序中的其他线程需要挂起,所以会进入STW(stop the world)状态,产生停顿,影响程序的正常运行。所以垃圾回收的调优就是尽量减少FGC的次数。

现有的垃圾回收器

年轻代垃圾回收器

Serial 单线程,串行回收,垃圾回收时其他线程将挂起

Parallel Scavange 并行回收,多个线程进行垃圾回收,但是主程序的线程也需要挂起

Parallel New Parallel Scavenge的升级版,为了配合老年代回收器CMS回收器

老年代垃圾回收器

Serial old 单线程

Parallel old 多线程

CMS(ConcurrentMarkSweep) 高效率老年代垃圾回收算法,STW时间短

其他垃圾回收器

G1、ZGC、Shenandoah、Eplison。

JVM命令行分类

标准命令 -:所有hotspot虚拟机都支持

特定命令 -x:特定的hotspot版本支持

不稳定命令 -XX:下个版本可能取消

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值