日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

在 C++ 中实现一个轻量的标记清除 gc 系统

發(fā)布時(shí)間:2025/3/15 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在 C++ 中实现一个轻量的标记清除 gc 系统 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在 C++ 中實(shí)現(xiàn)一個(gè)輕量的標(biāo)記清除 gc 系統(tǒng)

最近想把 engine 做一個(gè)簡(jiǎn)單 C++ 封裝,結(jié)合 QT 使用。engine 本身是用純 C 實(shí)現(xiàn)的,大部分應(yīng)用基于 lua 開發(fā)。對(duì)對(duì)象生命期管理也依賴 lua 的 gc 系統(tǒng)。關(guān)于這部分的設(shè)計(jì),可以參考我以前寫的一篇 為 lua 封裝 C 對(duì)象的生存期管理問題 。

當(dāng)我們把中間層搬到 C++ 中時(shí),遇到的問題之一就是,C++ 沒有原生的 gc 支持。我也曾經(jīng)寫過(guò)一個(gè) gc 庫(kù)。但在特定應(yīng)用下還不夠簡(jiǎn)潔。這幾天過(guò)年休息,仔細(xì)考慮了一下相關(guān)的需求,嘗試實(shí)現(xiàn)了一個(gè)更簡(jiǎn)單的 gc 框架。不到 200 行代碼吧,我直接列在這篇 blog 里。

這些尚是一些玩具代碼,我花了一天時(shí)間來(lái)寫。有許多考慮不周的地方,以及不完整的功能。但可以闡明一些基本思路。

首先我需要一個(gè)標(biāo)記清除的 gc 系統(tǒng),用來(lái)解決引用記數(shù)不容易解決的循環(huán)引用問題。它的實(shí)現(xiàn)不想比引用記數(shù)復(fù)雜太多,并有相同甚至更高的性能。

我不想使用復(fù)雜的 template 技術(shù),利用太多的語(yǔ)法糖讓使用看起來(lái)簡(jiǎn)單。如果需要讓這些 C++ 代碼看起來(lái)更現(xiàn)代,更有“品味”,我想也不是很難的事情。

接口和實(shí)現(xiàn)希望盡量分離,對(duì)用的人少暴露細(xì)節(jié)。但不拘泥于教條,強(qiáng)求做成類似 COM 那樣的通用 ABI 。還是盡量利用 C++ 語(yǔ)言本身提供的機(jī)制,不濫用。

使用盡量簡(jiǎn)單,不要讓使用人員有太大負(fù)擔(dān)。

功能滿足最低需求即可。代碼容易閱讀,使用人員可以很快理解原理,不至于誤用。也方便日后擴(kuò)展以適應(yīng)新的需求。

代碼如下:(可打包下載)

/*

?*? filename:? i_gcobject.h

?*? Copyright (c) 2010 ,

?*????? Cloud Wu . All rights reserved.

?*

?*????? http://www.codingnow.com

?*

?*? Use, modification and distribution are subject to the "New BSD License"

?*? as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

?*/

?

#ifndef interfacce_gcobject_h

#define interfacce_gcobject_h

?

#define interface struct

?

interface i_gcobject {

??? virtual ~i_gcobject() {}

??? virtual void touch() {}

??? virtual void mark() = 0 ;

??? virtual void grab() = 0 ;

??? virtual void release() = 0 ;

?

??? static void collect();

};

?

#endif

所有支持 gc 管理的接口都繼承至 i_gcobject ,提供三個(gè)方法,

1.??? mark 可以把這個(gè)對(duì)象打上標(biāo)記,被標(biāo)記的對(duì)象將不會(huì)被 collect 回收。

2.??? grab 將對(duì)象掛接到一個(gè)被稱呼為 root 的特殊 gcobject 上。

3.??? release 將對(duì)象從 root 上取掉。

另提供 touch 的模板方法供 mark 回調(diào),用來(lái)標(biāo)記同一對(duì)象中的不同部分。

mark 方法一般在 touch 方法中使用,另外,collect 方法將主動(dòng)調(diào)用 root 的 mark 。


/*

?*? filename:? i_gcholder.h

?*? Copyright (c) 2010 ,

?*????? Cloud Wu . All rights reserved.

?*

?*????? http://www.codingnow.com

?*

?*? Use, modification and distribution are subject to the "New BSD License"

?*? as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

?*/

?

#ifndef interfacce_gcholder_h

#define interfacce_gcholder_h

?

#include "i_gcobject.h"

?

interface i_gcholder : virtual i_gcobject {

??? virtual void hold(i_gcobject *) = 0;

??? virtual void unhold(i_gcobject *) = 0;

?

??? static i_gcholder * create();

};

?

#endif

i_gcholder 為 root 的接口,提供 hold 和 unhold 方法來(lái)掛接需要持久保留的 gcobject 。


/*

?*? filename:? gcobject.h

?*? Copyright (c) 2010 ,

?*????? Cloud Wu . All rights reserved.

?*

?*?? ???http://www.codingnow.com

?*

?*? Use, modification and distribution are subject to the "New BSD License"

?*? as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

?*/

?

#ifndef gc_object_h

#define gc_object_h

?

#include "i_gcobject.h"

?

class gcobject : virtual i_gcobject {

??? bool marked;

public:

??? gcobject();

??? virtual void mark();

??? virtual void grab();

??? virtual void release();

??? struct f_unmarked;

};

?

#endif

/*

?*? filename:? gcobject.cpp

?*? Copyright (c) 2010 ,

?*????? Cloud Wu . All rights reserved.

?*

?*????? http://www.codingnow.com

?*

?*? Use, modification and distribution are subject to the "New BSD License"

?*? as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

?*/

?

#include "gcobject.h"

#include "i_gcholder.h"

?

#include <vector>

#include <algorithm>

?

static bool gc_trigger;

static std::vector<gcobject *> gc_pool;

static i_gcholder * gc_root = i_gcholder::create();

?

struct gcobject::f_unmarked {

??? bool operator() (gcobject * value) {

??? ????bool unmarked = value->marked != gc_trigger;

??????? if (unmarked) {

??????????? delete value;

??????? }

??????? return unmarked;

??? }

};

?

gcobject::gcobject() : marked(!gc_trigger)

{

??? gc_pool.push_back(this);

}

?

void

gcobject::mark()

{

??? if (marked != gc_trigger) {

??????? marked = gc_trigger;

??????? touch();

??? }

}

?

void

gcobject::grab()

{

??? gc_root->hold(this);

}

?

void

gcobject::release()

{

??? gc_root->unhold(this);

}

?

void

i_gcobject::collect()

{

??? gc_root->mark();

?

??? gc_pool.erase(remove_if(gc_pool.begin(), gc_pool.end() , gcobject::f_unmarked()), gc_pool.end());

?

??? gc_trigger = !gc_trigger;

}

gcobject 為具體的 gc 實(shí)現(xiàn),實(shí)現(xiàn)了 mark 、grab、release 和 collect 方法。

1.??? mark 采用的直接向一 bool 變量設(shè)置標(biāo)記。這個(gè)標(biāo)記利用了 trigger 這個(gè)乒乓開關(guān),每次 collect 都會(huì)切換狀態(tài)。

2.??? grab 和 release 可以把對(duì)象掛接到 root 上,或從上取掉。

3.??? collect 會(huì)主動(dòng)從 root 開始 mark ,并釋放那些沒有 mark 的對(duì)象。


/*

?*? Copyright (c) 2010 ,

?*????? Cloud Wu . All rights reserved.

?*

?*????? http://www.codingnow.com

?*

?*? Use, modification and distribution are subject to the "New BSD License"

?*? as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

?*/

?

#include "i_gcholder.h"

#include "gcobject.h"

#include <vector>

#include <algorithm>

#include <cassert>

?

class gcholder : public virtual i_gcholder, virtual gcobject {

??? std::vector<i_gcobject *> hold_set;

??? std::vector<i_gcobject *> unhold_set;

??? bool set_changed;

??? bool hold_set_sorted;

??? bool unhold_set_sorted;

??? void combine_set();

??? virtual void touch();

??? virtual void hold(i_gcobject *obj) {

??????? hold_set.push_back(obj);

??????? hold_set_sorted = false;

??????? set_changed = true;

??? }

??? virtual void unhold(i_gcobject *obj) {

??????? unhold_set.push_back(obj);

??????? unhold_set_sorted = false;

??????? set_changed = true;

??? }

??? struct f_mark {

??????? void operator() (i_gcobject *obj) {

??????????? obj->mark();

??????? }

??? };

public:

??? gcholder() :

??????? set_changed(false),

??????? hold_set_sorted(true) ,

??????? unhold_set_sorted(true) {}

};

?

void

gcholder::combine_set()

{

??? if (!hold_set_sorted) {

??????? std::sort(hold_set.begin(),hold_set.end());

??????? hold_set_sorted = true;

??? }

??? if (!unhold_set_sorted) {

??????? std::sort(unhold_set.begin(),unhold_set.end());

??????? unhold_set_sorted = true;

??? }

??? if (!unhold_set.empty()) {

??????? std::vector<i_gcobject *>::iterator iter1 = hold_set.begin();

??????? std::vector<i_gcobject *>::iterator iter2 = unhold_set.begin();

??????? while (iter1 != hold_set.end() && iter2 != unhold_set.end()) {

??????????? if (*iter1 == *iter2) {

??????????????? *iter1 = NULL;

??????????????? ++iter1;

??????????????? ++iter2;

??????????? }

??????????? else {

??????????????? assert(*iter1 < *iter2);

??????????????? ++iter1;

??????????? }

??????? }

??????? i_gcobject * null = NULL;

??????? hold_set.erase(std::remove(hold_set.begin(),hold_set.end(),null) , hold_set.end());

??????? unhold_set.clear();

??? }

}

?

void

gcholder::touch()

{

??? if (set_changed) {

??????? combine_set();

??????? set_changed = false;

??? }

?

??? std::for_each(hold_set.begin(), hold_set.end(), f_mark());

}

?

i_gcholder *

i_gcholder::create()

{

??? return new gcholder;

}

gcholder 理論上可以有多個(gè)實(shí)例,并相互掛接。(否則不需要繼承至 i_gcobject )這個(gè)設(shè)計(jì)可以用來(lái)模擬多級(jí)的堆棧。但實(shí)際上并不需要這么復(fù)雜。因?yàn)樵诖蟛糠謶?yīng)用里,如果你的程序有一個(gè)周期性的主循環(huán),就可以不在 gc 系統(tǒng)里模擬出一個(gè)多級(jí)的堆棧。我們只用在循環(huán)之外做 collect 即可。再堆棧調(diào)用的較深層次觸發(fā) collect 反而效果不佳,會(huì)導(dǎo)致許多臨時(shí) gc 對(duì)象無(wú)法回收。


最后來(lái)看一個(gè)玩具代碼,用 stl 里的 mutliset 實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的樹接口。可能沒有什么使用價(jià)值,但它演示了一個(gè)較復(fù)雜的對(duì)象相互引用的關(guān)系。并可以展示 gc 如何正確工作。

/*

?*? filename:? test.cpp

?*? Copyright (c) 2010 ,

?*????? Cloud Wu . All rights reserved.

?*

?*????? http://www.codingnow.com

?*

?*? Use, modification and distribution are subject to the "New BSD License"

?*? as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

?*/

?

#include "gcobject.h"

?

#include <cstdio>

#include <set>

#include <algorithm>

?

interface i_tree : virtual i_gcobject {

??? virtual void link(i_tree *p) = 0;

?

??? static i_tree * create();

};

?

class tree : public virtual i_tree , virtual gcobject {

??? tree *parent;

??? std::multiset<tree *> children;

?

??? struct f_mark {

??????? void operator() (tree *node) {

??????????? node->mark();

??????? }

??? };

?

??? virtual void touch() {

??????? if (parent)

??????????? parent->mark();

?

??????? std::for_each(children.begin(), children.end(), f_mark());

??? }

?

??? void unlink();

?

??? virtual void link(i_tree *parent);

public:

??? tree() : parent(NULL) {

??????? printf("create node %p\n",this);

??? }

?? ?~tree() {

??????? printf("release node %p\n",this);

??? }

};

?

void

tree::unlink()

{

??? if (parent) {

??????? parent->children.erase(this);

?

??????? parent = NULL;

??? }

}

?

void

tree::link(i_tree *p)

{

??? unlink();

??? if (p) {

??????? tree * cp = dynamic_cast<tree *>(p);

??????? cp->children.insert(this);

??????? parent = cp;

??? }

}

?

i_tree *

i_tree::create()

{

??? return new tree;

}

?

int

main()

{

??? i_tree *root = i_tree::create();

?

??? root->grab();

?

??? i_tree *node;

?

??? node = i_tree::create();

??? node->link(root);

?

??? node = i_tree::create();

??? node->link(root);

?

??? i_gcobject::collect();

?

??? printf("collected\n");

??? node->link(NULL);

?

??? i_gcobject::collect();

?

??? printf("finalize\n");

?

??? root->release();

?

??? i_gcobject::collect();

?

??? return 0;

}


我們?cè)趯?shí)現(xiàn)一個(gè)基于 gc 的對(duì)象時(shí),可以先定義出需要的接口,讓接口從 i_gcobject 繼承。例如上例中的 i_tree 。

然后在實(shí)現(xiàn)這個(gè)接口時(shí),可以虛繼承 gcobject 。例如上例中的 tree 。

如果有需要,就重載 touch 方法,在 touch 方法中 mark 相關(guān)的 gcobject 。對(duì)于 tree 這個(gè)例子,就是調(diào)用父親和孩子節(jié)點(diǎn)的 mark 。

對(duì)象依然可以寫析構(gòu)函數(shù),相當(dāng)于對(duì)象的 finalize 。在析構(gòu)函數(shù)中,不要再釋放和它相關(guān)的 gcobject ,那些留給 gc 系統(tǒng)去完成。(例如在 tree 里,就不要在 ~tree 中 delete children 容器中的變量,也不需要把自己從父親節(jié)點(diǎn)上摘掉)


如果僅僅只是使用那些接口,則不需要再包含 gcobject.h ,因?yàn)?gcobject 的細(xì)節(jié)只供實(shí)現(xiàn) i_gcobject 時(shí)使用。

?

總結(jié)

以上是生活随笔為你收集整理的在 C++ 中实现一个轻量的标记清除 gc 系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。