Docker 上で動いている C++ プログラムのメモリ使用量が大きく、挙動も重そうだったので heaptrack を使って確認した。
まずは素直に heaptrack を実行。
heaptrack ./a.out
相対パスを付けないと実行ファイルとして認識されない点に注意。
実行自体は成功し、heaptrack.*.raw.gz が生成されたが、指示通りに interpret すると以下のようなエラーが出た。
failed to parse line: m ... allocation index out of bounds Segmentation fault
raw ファイル自体は壊れておらず、gzip -t は通る。
試行錯誤の結果、以下の形でのみ正しく interpret できた。
zcat heaptrack.xxx.raw.gz \ | /usr/local/lib/heaptrack/libexec/heaptrack_interpret \ | gzip -c > heaptrack.xxx.gz
gzip -dc 経由では失敗し、zcat 経由だと成功する。
理由は深追いしていないが、少なくともこの形は安定した。
Windows 上で GUI を確認するために WSL2 + WSLg を使用。
Qt の xcb 依存が不足していると起動しないので以下を追加。
sudo apt install \ libxkbcommon-x11-0 \ libxcb-icccm4 \ libxcb-image0 \ libxcb-keysyms1 \ libxcb-render-util0 \ libxcb-xinerama0
DISPLAY は自動設定されるので手動 export は不要。
heaptrack_gui heaptrack.xxx.gz
で GUI が起動。

最初に目についたのは Temporary Allocations のグラフ。 時間に対してほぼ直線的に増加している。
一見リークのように見えるが、Temporary Allocations なので「確保→解放」が繰り返されているだけ。
つまり、
- メモリが残り続けているわけではない
- malloc/free が異常な回数呼ばれている
という状態。
Temporary Allocations を詳細に見ると、以下が突出していた。
gnu_cxx::__sso_string_base::_M_construct<char*>

250 万回以上。
std::string の構築がホットループ内で大量に発生している。
典型的には以下のようなコードが原因になる。
std::string s = some_cstr;
log << "value=" << x;
SSO があっても、一時生成や連結では allocation が発生する。
ここは
- string_view 化
- reserve + 再利用
- ログの抑制
で大きく改善できる。
次に Consumed(常駐メモリ)を確認。
ピーク時の最大寄与は以下。
std::vector<...> 約 4.7MB

Temporary ではなく、実行中ずっと保持されているデータ構造。
- vector が grow し続けている
- clear されない
- 設計上の常駐データ
という挙動。
これはリークではなく仕様だが、
- 本当に全期間保持が必要か
- フェーズ境界で clear できないか
- reserve が適切か
は検討の余地あり。