1、概述
最近看 Protocal Buffer 的源碼,初次見到這個庫源自陳碩的 muduo ,便打算看一看,在此做一下記錄。官網文檔不能訪問,只能憑借代碼的自己理解,查看的源碼版本為 3.6.0。
初識 Arena 時,發現是個 allocator。Arena 每次分配一大塊內存,使用時在已經分配的內存塊上獲取,用來代替 new/delete 的堆上分配。通過一次申請大塊內存,並且一次性釋放,同時可取消調用對象的析構函數來提升效率。
Arena 線程安全:多個線程可並發在Arena分配空間。但銷毀時非線程安全,銷毀線程必須和Arena 的使用者同步。
2、特點
Arena 分配器使用 CreateMessage<T> 接口創建消息:
-類型 T 必須(至少)有兩個構造函數:1 不含參數的構造函數,當在堆上分配時使用; 2 含 google::protobuf::Arena* 參數的構造函數,在Arena上分配時使用。當第二種情況傳入空指針時,與第一種情況相同。
-類型 T 必須有特殊的特點:定義類型 |InternalArenaConstructable_|。一般被定義為 typedef void 類型。如果沒有會在編譯時報錯。實現在 InternalHelper 模板類中,具體代碼如下。
1 template <typename U> 2 static char ArenaConstructable(const typename U::InternalArenaConstructable_*); 4 template <typename U> 5 static double ArenaConstructable(...); 6 7 typedef std::integral_constant<bool, sizeof(ArenaConstructable<T>( 8 static_cast<const T*>(0))) == 9 sizeof(char)> 10 is_arena_constructable;
-類型T還 *可以* 有特征:| DestructorSkippable_|。如果該類型被定義,在傳入非空 Arena指針時,對象的析構函數不會被調用。如果沒有該特征,則在Arena銷毀時,對象的析構函數一定會被調用。實現方法與上面類似。
1 template <typename U> 2 static char DestructorSkippable(const typename U::DestructorSkippable_*); 3 template <typename U> 4 static double DestructorSkippable(...); 5 6 typedef std::integral_constant< 7 bool, sizeof(DestructorSkippable<T>(static_cast<const T*>(0))) == 8 sizeof(char) || 9 std::is_trivially_destructible<T>::value> 10 is_destructor_skippable;
在 proto 文件中, 設置 option cc_enable_arenas = true; 則使用 Arena 分配器。此時在使用 protoc 編譯器生成 pb.h 的代碼中會有 InternalArenaConstructable_ 和 DestructorSkippable_的定義,如
1 typedef void InternalArenaConstructable_; 2 typedef void DestructorSkippable_;
3、實現
Arena 的實現在 arena.h arena.cc arena_impl.h 中,同時還有專門的字符串域的實現在 arenastring.h 中。其中arena_impl 中是實現存儲分配的核心代碼,是arena中一個對象。
Arena 的內存結構如下圖所示,圖使用 Dia 繪制。每個線程都會分配一個Block,在Block上分配一個 SerialArena。Block是個鏈表結構,在空間分配時,如果該Block空間不夠用,則新建一個Block,新Block的大小是舊Block的二倍,同時新Block的next指向舊 Block。一個線程在獲取 Arena 時,如果該線程還沒分配 Block,則申請一個Block,在該Block上分配 SerialArena,該SerialArena 的 next 指向上個線程的 SerialArena。
在 ArenaImpl 銷毀時,首先進行 Cleanup,然后釋放所有空間。Cleanup是根據添加的 Cleanup 表,順序調用各個對象的 cleanup 函數,Cleanup 列表如下圖所示。釋放空間時會循環銷毀每個 SerialArena 的所有 Block。
暫時先寫到這里吧。