02. メモリリークの検出(C++)

C++版にはLive2Dの内部で確保したメモリに関して、リークしているかどうかを検出する機能があります。
具体的にはinit()からdispose()までにLive2Dが確保したメモリについての情報をダンプします。
※ この機能はDEBUGビルド時しか機能しません。 ( VisualStudioなら_DEBUG、XCodeならDEBUGマクロが有効の時)


出力例

----------------- Live2D Memory Info -----------------

Successfully cleaned (no memory leaks found).

local malloc count : remaining : 0

local malloc count : total : 48884

local malloc memory : current : 0 byte

local malloc memory : peak : 539 KB

local malloc memory : total : 1351 KB

allocator malloc count : remaining : 0

allocator malloc count : total : 109

allocator malloc memory : current : 0 byte

allocator malloc memory : peak : 604 KB

allocator malloc memory : total : 802 KB

LDObject instance count : 1

LDObject total count : 47303

LDUnmanagedObject instance count : 0

LDUnmanagedObject total count : 1

------------------------------------------------------

出力のうち重要なのは

local malloc count : remaining

allocator malloc count : remaining

の項目です。
これらが0になっていない場合はLive2D関連のオブジェクトの解放忘れがあります。


Live2Dのメモリ構造について

Live2D SDKの仕様として、モデルやモーション間でID情報は共有する構造になっています。
新しいモデル(またはモーション)を読み込んだ際、これまで1度も使われていないIDの場合は新たにIDを保持します。
モデル(モーション)を破棄した後も元のサイズには戻らずリークした印象となります。
同じモデルをロード・削除する度にメモリ使用量が増える場合はメモリリークと思われますが、初回ロード時しかメモリが増えない場合それは正常な動作となります。

また、事前にモデル・モーションでのIDの使用数が推測しにくく、ダウンロードによる新規モデル追加などのケースも有り得るため現状では大きめに確保するよりは、必要に応じて確保する方式を採っています。
Live2D内部のメモリ管理では、共通領域、各モデル、各モーション、それぞれに異なる領域にまとめてデータを確保・破棄していく事で断片化が発生しにくい仕組みになっています。
modelAとmodelBの2つがいた場合、modelAのメモリ領域は、modelB のメモリ領域と完全に分離されています。
modelAを破棄した際は、modelAに使われていたメモリ領域が完全に再利用可能になります。
modelA 1つが利用するメモリ領域も単一の連続領域で確保するのではなく、複数のページ(最低4KBのまとまったメモリ領域)で構成されております。各ページから実際のポインタサイズに応じて分割利用します。
より大きなメモリを使用するmodelCを後からロードする場合も、より小さなmodelAのメモリ領域を再利用しつつ、足りない分だけページを確保してロード可能となります。