C++ 中 RAII 与垃圾回收的区别

2025 年 3 月 18 日 | 阅读 7 分钟

RAII(Resource Acquisition Is Initialization,资源获取即初始化)和垃圾回收(GC)是两种有用的技术,它们在资源管理方面,尤其是在内存管理方面,发挥着相反的作用。在本文中,我们将讨论 RAII 和垃圾回收之间的区别。在讨论它们的区别之前,我们必须先了解 C++ 中的 RAII 和垃圾回收。

C++ 中的 RAII 是什么?

如前所述,RAII(Resource Acquisition Is Initialization)是 C++ 中一种广为人知的资源管理设计模式。它确保内存、文件句柄、套接字或任何其他系统资源能够以构造性和异常安全的方式被正确地分配和释放。RAII 将资源的操作与对象的销毁相关联,从而提供了一种确定性和安全性的资源使用方法。

RAII 的关键概念

RAII 的几个关键概念如下:

1. 构造函数中的资源获取

在 RAII 中,资源通常在对象构造时(作为分配的一部分)被获取,通常通过类的构造函数。这意味着一旦对象被创建,它就拥有了该资源,从而锁定了该资源。

这种资源可以是任何需要管理和控制的、并且如果不妥善管理可能会导致问题的对象,例如通过 `new` 分配的内存、新的文件描述符、新的套接字、新的数据库连接等。

2. 析构函数中的资源释放

对象拥有一个析构函数,当对象的生命周期结束、对象离开作用域或对象被删除时,析构函数会被自动调用,从而释放资源。

这使得资源的销毁具有确定性,减少了在异常发生时资源未能释放的可能性。

RAII 的优点

RAII 的优点如下:

1. 异常安全性

RAII 确保即使在发生异常的情况下,资源也能得到清理。由于资源与对象的生命周期相关联,当对象离开作用域时,无论控制流是正常的还是由于异常导致的异常,其析构函数都会被调用。这避免了资源泄露问题,因为清理内存、文件句柄或其他系统资源的工作由析构函数完成。

2. 确定性的销毁

RAII 提供确定性的销毁,这意味着资源会在一个特定的时间点被释放,即对象作用域的结束。这与垃圾回收系统中非确定性的资源释放不同。在 RAII 中,销毁是即时的,开发者可以控制资源释放的时机。

3. 无需手动清理

RAII 有效地将开发者从手动管理资源的任务中解放出来。资源在构造函数中被获取,在析构函数中被释放。因此,最大限度地减少了人为错误的几率,包括忘记释放内存或关闭文件。这种自动处理使得代码更少出错,更不容易出现内存泄漏或悬空指针等问题。

程序

下面我们用一个例子来说明 C++ 中的 RAII。

示例

编译并运行

输出

 
Resource acquired
Using the resource
Inside shared function
Using the resource
Resource released   

什么是垃圾回收?

垃圾回收(GC) 可以被描述为运行时系统找出程序不再使用的内存空间并将其释放的过程。C++ 本身没有像 JavaC# 那样的 GC,但开发者可以使用第三方库或框架在 C++ 中实现 GC。大多数情况下,C++ 中的资源管理是通过 `new` 和 `delete` 操作符手动完成的,而 RAII(如果未使用)则通过处理智能指针和一些库(如 Boehm-Demers-Weiser 垃圾回收器)来实现垃圾回收功能。

自动内存管理: 垃圾回收能够知道程序使用了多少内存,然后丢弃那些不再有用的内存。它消除了开发者需要花费时间手动释放内存的需求。

可达性: 垃圾回收器通过识别“存活”对象(即程序仍然引用的对象)和“垃圾”对象(程序中不再使用的对象)来工作。

GC 算法

GC 技术包括以下几种垃圾回收算法:

  1. 标记-清除(Mark and Sweep): GC 标记所有可达对象,清除不可达对象,然后释放内存空间。
  2. 引用计数(Reference Counting): 记录每个对象的引用次数,当引用计数降为零时,对象被释放。
  3. 分代 GC(Generational GC): 对象通常按代划分,较年轻的对象会被更频繁地收集。
  4. 非确定性: 与 RAII 不同,GC 不指定何时释放内存。因此,可能需要比预期更长的时间来释放内存。它是一个周期性触发的收集器,内存释放取决于运行环境。

垃圾回收的优点

垃圾回收的优点如下:

  1. 简化的内存管理: 垃圾回收(GC)通过收回不再需要的内存来自动执行内存管理。它为开发者节省了大量手动`释放`内存的时间,从而最大限度地减少了内存泄漏、重复释放或访问已释放内存(即“悬空指针”)的可能性,这些问题在手动内存管理中可能会出现。
  2. 减少内存泄漏: GC 消除了内存泄漏的可能性,即程序未能释放不再需要的内存空间。垃圾回收还便于识别空闲内存并释放它们,使其不被未使用的内存占用,从而避免了因不断累积未使用内存而导致的性能下降。
  3. 开发便捷: 即使在大型或复杂的应用程序中,GC 的自动内存管理也减轻了开发者的负担。开发者可以专注于实现功能的代码,而无需担心复杂且耗时的内存管理。
  4. 错误更少: 垃圾回收器消除了简单的内存管理错误,例如忘记释放内存块或访问已删除的对象。由于 GC 负责内存回收,它有助于减少与内存误用相关的 bug 的发生,从而提供更稳定和可靠的代码。

总而言之,垃圾回收通过无需显式进行复杂且易出错的内存分配,有助于提高代码的效率和质量。

RAII 与垃圾回收的关键区别

Difference Between RAII and Garbage Collection in C++

RAII 和垃圾回收之间存在几个关键区别。一些主要区别如下:

方面RAII垃圾回收
内存管理RAII 是确定性的和显式的。因此,资源的释放基于对象的生命周期。构造函数用于获取资源,析构函数用于释放资源,从而实现明确的清理。GC 以自动存储分配和去分配的方式提供服务,内存会在固定的时间间隔内由垃圾回收器自动回收。
释放时间一旦对象的生命周期结束,就会发生去分配以释放资源,这是灵活的,因为它直接决定了何时发生去分配。由于垃圾回收器的不可预测的操作,去分配也会在意想不到的时间间隔发生,这导致了非确定性的内存管理。
资源清理垃圾回收仅限于由 RAII 所有权模型管理的对象的显式析构函数。资源去分配由垃圾回收器处理,它会在发现对象不再需要时回收对象并释放内存。
错误预防RAII 有助于避免资源泄漏和其他问题,包括内存泄漏、重复释放或对已释放内存的剩余引用,即所谓的悬空指针。GC 通过立即查找和回收不再使用的内存,最大限度地减少了内存泄漏等常见风险。
用途RAII 可用于管理其他资源,如内存、文件句柄、互斥量和网络连接。因此,可以满足所有资源管理需求。GC 主要关注内存管理,旨在回收分配给对象的内存。非内存资源的使用需要轻松控制,否则需要执行其他机制。