首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

C++ 智能指針實現垃圾回收:引用計數、循環引用與線程安全

     編輯:Java 相關知識

背景:C++ 手動內存管理痛點

C++ 要求 `new`/`delete` 配對,複雜對象圖中易發生內存洩漏、雙重釋放、懸空指針。Java/C# 垃圾回收器自動管理,但 C++ 作為編譯型語言無運行時 GC,需庫級實現智能指針。

第一版:簡單引用計數 auto_ptr

  • 全局 `CPtrManager` 維護 `void* → (refCount, mark)` 映射
  • 重載全局 `operator new/delete` 註冊/註銷分配塊
  • `auto_ptr` 構造/拷貝/賦值/析構時增減引用計數,歸零時 `delete`

缺陷:無法處理循環引用(A→B, B→A 計數永不歸零)、非線程安全。

循環引用(互鎖)檢測與解鎖算法

  1. 識別堆上智能指針:`CPtrManager::GetAutoMark(ptr)` 通過地址範圍判斷指針對象是否位於某個受管理堆塊內
  2. 構建候選集:收集所有「引用計數>0 且所有持有者均在堆上」的塊(即無棧上根引用)
  3. 去除偽循環:迭代刪除持有者包含非候選集指針的塊,剩下的為真循環組
  4. 分組釋放:對每個強連通分量調用 `Release()` 遞歸釋放
  5. 後台線程定期掃描:`CMarkTable::DoLockMark()` 在單獨線程執行,持有 `CPtrManager` 寫鎖,避免干擾主線程

線程安全增強

  • `CPtr` 內部 `CMutex` 保護單塊操作
  • `CPtrManager` 全局 `CMutex` 保護映射表
  • 掃描階段 `bWrite` 標志獨佔寫入,讀操作繼續(讀寫分離可進一步優化)

現代 C++ 最佳實踐:標準庫智能指針

C++11 引入標準智能指針,**生產代碼首選標準庫**,手寫僅教學/特殊場景:

shared_ptr + weak_ptr 破循環示例


struct Node {
    std::shared_ptr next;
    std::weak_ptr prev; // 弱引用,不增加計數
    ~Node() { std::cout << "Node destroyed\n"; }
};

void test() {
    auto a = std::make_shared();
    auto b = std::make_shared();
    a->next = b;
    b->prev = a; // 弱引用
    // 作用域結束,a/b 計數歸零,正常析構
}

自定義刪除器與數組支持


// 文件句柄 RAII
auto fp = std::shared_ptr(fopen("data.txt","r"), fclose);

// 數組
auto arr = std::unique_ptr(new int[100]);
arr[5] = 42;

性能與避坑提示

  • `make_shared` 單次分配對象+控制塊,比 `new` + `shared_ptr` 構造快、內存局部性好
  • 避免循環引用:設計時明確所有權方向,必要處用 `weak_ptr`
  • 多線程讀寫 `shared_ptr` 控制塊原子操作無鎖,**但指向對象的訪問需自行同步**
  • 不要混用裸指針與智能指針管理同一對象
  • 自定義 `deleter` 類型會成為 `shared_ptr` 類型一部分,影響容器存儲

結語

手寫 GC 智能指針展示了引用計數、循環檢測、線程同步核心機制,但現代 C++ 開發應直接使用標準庫 `unique_ptr`/`shared_ptr`/`weak_ptr` 組合,配合 RAII 原則,實現安全、高效、無 GC 停頓的確定性內存管理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類型所有權語義適用場景循環引用解決
`std::unique_ptr`獨占,不可拷貝可移動單一所有者、RAII 資源管理無循環(無共享)
`std::shared_ptr`共享,引用計數(控制塊原子操作)多所有者、緩存、觀察者`std::weak_ptr` 打破循環
`std::weak_ptr`非所有權觀察者,不增加計數緩存、父子雙向引用、打破循環配合 `shared_ptr` 使用