垃圾回收机制

一、什么是垃圾回收

在计算机程序运行过程中,我们的程序会不断地申请内存、使用内存和释放内存,但有些时候,我们无法确定什么时候释放内存,导致内存泄露问题。为了解决这一问题,垃圾回收机制应运而生。

垃圾回收机制是一种自动化管理内存的机制,其主要作用是通过监控内存的使用情况、识别不再使用的内存和释放内存,使我们的程序更加高效和可靠。

二、常见的垃圾回收算法

垃圾回收算法是垃圾回收机制的核心。常见的垃圾回收算法有三种:

1.引用计数算法

引用计数算法是一种最简单的垃圾回收算法,其原理是通过维护每个对象的引用计数,当对象引用计数为零时,就将其释放。


class Object {
public:
    Object() : mRefCount(0) {}
    virtual ~Object() {}
    
    void addRef() {
        mRefCount++;
    }
    
    void release() {
        mRefCount--;
        if (mRefCount <= 0) {
            delete this;
        }
    }

private:
    int mRefCount;
};

引用计数算法的优点是实现简单、实时回收;但其缺点是不能解决循环引用的问题,且增加引用计数和减少引用计数会增加额外的开销。

2.标记清除算法

标记清除算法是一种基于“可达性分析”(Reachability Analysis)的垃圾回收算法。其流程如下:

从根节点开始遍历所有对象,标记所有可达对象(即不能被回收的对象),未标记的对象即为可回收对象,进行清除。如果对象存在循环引用,则标记算法无法识别出可回收对象,即存在内存泄露问题。


void mark(Object* object) {
    if (!object || object->mMarked) {
        return;
    }
    object->mMarked = true;
    for (auto it = object->mReferences.begin(); it != object->mReferences.end(); ++it) {
        mark(*it);
    }
}

void sweep() {
    for (auto it = Object::sObjects.begin(); it != Object::sObjects.end(); ++it) {
        if (!(*it)->mMarked) {
            delete *it;
            *it = nullptr;
        } else {
            (*it)->mMarked = false;
        }
    }
}

void gc() {
    for (auto it = Object::sRoots.begin(); it != Object::sRoots.end(); ++it) {
        mark(*it);
    }
    sweep();
}

3.复制算法

复制算法是一种基于“分代假设”(Generational Hypothesis)的垃圾回收算法。其流程如下:

将内存分为两个区域,一个为“From Space”,一个为“To Space”。程序在From Space分配内存,并在使用完之后,将不再使用的对象拷贝到To Space。当To Space已经存储了足够多的对象时,执行“数代推进”(generational promotion),即将To Space中的所有对象移动到From Space中。该算法能够解决循环引用的问题,效率也相对较高,但同时需要使用更多的内存。


void gc() {
    Object* fromSpace = currentHeap->getFromSpace();
    Object* toSpace = currentHeap->getToSpace();
    Object* p = nullptr;
    for (auto it = Object::sRoots.begin(); it != Object::sRoots.end(); ++it) {
        copy(&p, (*it));
        *it = p;
    }
    while (fromSpace getAllocPtr()) {
        Object* object = reinterpret_cast<Object*>(fromSpace);
        fromSpace += object->getSize();
        copy(&p, object);
        object = p;
    }
    std::swap(fromSpace, toSpace);
    currentHeap->setAllocPtr(fromSpace);
}

三、如何选择合适的垃圾回收算法

不同的垃圾回收算法适用于不同的场景。我们需要根据自己的需求来选择合适的算法。

引用计数算法适用于单线程环境下的小对象,但不能解决循环引用的问题;标记清除算法适用于多线程环境下和连续分配内存的场景下,但容易出现内存碎片问题;复制算法适用于多线程环境下和存在大量不活跃(常驻内存中)对象的场景下,在其他场景下可能会浪费内存。

四、垃圾回收相关的实践

垃圾回收机制在现代编程语言中得到了广泛的应用。例如,Java和C#都采用了垃圾回收机制,Python、Ruby等脚本语言也采用了此类机制。下面是一个基于C++11的垃圾回收框架:


#include <iostream>
#include <list>

class Object {
public:
    static std::list<Object*> sObjects;
    static std::list<Object*> sRoots;
    
    Object() {
        sObjects.push_back(this);
    }
    
    virtual ~Object() {}
    
    void* operator new(size_t size) {
        void* p = malloc(size);
        currentHeap->setAllocPtr(static_cast<char*>(p) + size);
        return p;
    }

    void* operator new[](size_t size) {
        void* p = malloc(size);
        currentHeap->setAllocPtr(static_cast<char*>(p) + size);
        return p;
    }

    void operator delete(void* p) {
        if (!currentHeap->contains(p)) {
            free(p);
        }
    }

    void operator delete[](void* p) {
        if (!currentHeap->contains(p)) {
            free(p);
        }
    }

    virtual int getSize() = 0;

    static void setHeap(void* start, void* end) {
        currentHeap = new Heap(start, end);
    }

private:
    static class Heap* currentHeap;
};

std::list<Object*> Object::sObjects;
std::list<Object*> Object::sRoots;
class Heap* Object::currentHeap = nullptr;

class Heap {
public:
    Heap(void* start, void* end) : mStart(start), mEnd(end), mAllocPtr(start) {}

    bool contains(void* p) {
        return p >= mStart && p  mEnd - highWaterMark) {
            gc();
        }
    }

    void* getAllocPtr() const {
        return mAllocPtr;
    }

    Object* getFromSpace() {
        return reinterpret_cast<Object*>(mStart);
    }

    Object* getToSpace() {
        return reinterpret_cast<Object*>(mEnd - highWaterMark);
    }

private:
    void gc() {
        std::cout << "Start GC...\n";
        Object** oldRoots = new Object*[Object::sRoots.size()];
        int pos = 0;
        for (auto it = Object::sRoots.begin(); it != Object::sRoots.end(); ++it) {
            oldRoots[pos++] = *it;
        }
        for (int i = 0; i < pos; ++i) {
            mark(oldRoots[i]);
        }
        sweep();
        currentHeap->setAllocPtr(currentHeap->getToSpace());
        delete[] oldRoots;
        std::cout << "End GC...\n";
    }

    void mark(Object* obj) {
        if (!obj || obj->mMarked) {
            return;
        }
        obj->mMarked = true;
        for (auto it = obj->mReferences.begin(); it != obj->mReferences.end(); ++it) {
            mark(*it);
        }
    }

    void sweep() {
        for (auto it = Object::sObjects.begin(); it != Object::sObjects.end(); ) {
            if (!(*it)->mMarked) {
                delete *it;
                *it = nullptr;
                it = Object::sObjects.erase(it);
            } else {
                (*it)->mMarked = false;
                ++it;
            }
        }
    }

    void* mStart;
    void* mEnd;
    void* mAllocPtr;
    static const int highWaterMark = 2048;
};

class String : public Object {
public:
    static String* create(const char* str) {
        String* p = new String();
        p->mLength = strlen(str);
        p->mData = new char[p->mLength + 1];
        strncpy(p->mData, str, p->mLength + 1);
        return p;
    }

    ~String() {
        delete[] mData;
    }

    int getSize() override {
        return sizeof(String) + mLength + 1;
    }

    void addRef() {
        Object::addRef();
        std::cout << "Add reference...refCount=" << mRefCount << "\n";
    }

    void release() {
        Object::release();
        std::cout << "Release reference...refCount=" << mRefCount << "\n";
    }

private:
    String() : mData(nullptr), mLength(0) {
        Object::sRoots.push_back(this);
    }

    char* mData;
    int mLength;
};

int main() {
    Object::setHeap(malloc(1024), static_cast<char*>(malloc(1024)) + 1024);
    String* str1 = String::create("hello, world!");
    String* str2 = String::create("hello, AI!");
    str1->addRef();
    str1->addRef();
    str1->release();
    Object::sRoots.clear();
    Object::sRoots.push_back(str2);
    gc();
    free(Object::sObjects.front()); // free heap allocated by `malloc`
    Object::sObjects.pop_front();
    free(Object::sRoots.front()); // free heap allocated by `malloc`
    Object::sRoots.pop_front();
    free(Object::currentHeap);
    return 0;
}

该垃圾回收框架支持自动、实时、引用循环等情况下的内存回收。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/286433.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-23 03:47
下一篇 2024-12-23 03:47

相关推荐

  • Spring S_CSRF防护机制实现及应用

    Spring S_CSRF防护机制是Spring Security框架提供的一个针对跨站请求伪造攻击(CSRF)的保护机制。本文将从以下几个方面详细介绍Spring S_CSRF防…

    编程 2025-04-28
  • Python的垃圾回收机制

    本文将对Python的垃圾回收机制进行详细阐述,着重介绍它的基本原理和实现方式。此外,我们还将介绍常见的问题及解决方法,并给出相应的代码示例。 一、Python的垃圾回收概述 垃圾…

    编程 2025-04-27
  • Docker 垃圾电脑的解决方案

    Docker 是一种轻量级的容器化技术,可以在一个操作系统中,同时运行多个独立的应用。在使用 Docker 的过程中,可能会出现 Docker 占用大量硬盘空间,导致电脑变得极其缓…

    编程 2025-04-27
  • 机制与策略分离

    了解机制与策略分离的解决方法与优势 一、概述 机制与策略分离是一种软件设计理念,它将复杂的系统、组件等模块化,通过分离机制与策略,把模块实现的方式与具体使用方式分开。 机制是实现某…

    编程 2025-04-27
  • Python垃圾回收的实现机制与优化

    一、垃圾回收工作的原理 Python解释器采用了自动内存管理机制,即通过垃圾回收来自动管理内存。垃圾回收是python的一项基础服务,用于回收那些无用的内存。Python中的垃圾回…

    编程 2025-04-25
  • Android Binder机制详解

    一、Binder机制概述 Binder是一种进程间通信机制,它是Android系统中非常重要的一部分。在Android系统中,应用程序需要和设备驱动程序、系统服务等进程进行通信,这…

    编程 2025-04-24
  • Java 垃圾回收器详解

    一、垃圾回收器概述 Java 的垃圾回收机制可以自动回收程序中不用的对象,使开发者从手动释放内存的繁琐任务中解脱出来。Java垃圾收集器运行于Java虚拟机中,负责回收内存中的无用…

    编程 2025-04-24
  • 深入浅出Spring事务传播机制

    一、事务概念 事务是指作为单个逻辑工作单元执行的一系列操作,所有操作要么全部成功完成,要么全部失败而回滚。在关系型数据库中,事务通常是指一系列的数据操作,比如增删改查等。 二、Sp…

    编程 2025-04-18
  • 从多个方面详细阐述Redis缓存机制

    一、Redis缓存机制概述 Redis是一个高性能的key-value存储系统,同时也是一个非常好的缓存系统。在Web应用中,我们通常使用Redis作为缓存来提高Web应用的数据访…

    编程 2025-04-12
  • iOS WKWebView缓存机制详解

    一、WKWebView简介 WKWebView是苹果公司在2014年WWDC(苹果开发者大会)上发布iOS 8之后推出的新一代WebView。相较于之前的UIWebView,WKW…

    编程 2025-04-12

发表回复

登录后才能评论