[UE4] MallocLeakReporterを使用したメモリトラッキング


検証Ver:4.24.3

1. 結論

 この方法ではメモリーリークを必ず特定できるとは限らないため機能の一例としてご参考ください。
ということで、そこまで技術的な深堀はありません。たまには
この機能は「アンリアル エンジン 4 のメモリリーク対策」で紹介されているものですが、この記事が書かれた2016年から4年も経てば変わることもありますね…。

 メモリトラッキングをしたいのであれば以下のツールや方法をご利用頂くことをおすすめします。

[UE4] LLM (Low Level Memory Tracker)を使用したメモリトラッキング
[UE4] Malloc Profiler/Memory Profiler2を使用したメモリトラッキング
[UE4] MemProを使用したメモリトラッキング

2. 概要

 MallocLeakReporterはmemreportを利用して差分を取ることによるメモリリーク検出のための機能です。

[制約事項]
・MallocBinned2に対応 (MallocBinned3では利用不可)
 → 4.24の現時点ではWindowsでは利用可能です

 デフォルトでMallocBinned3を使用するプラットフォームは以下の定義を追加してBinned2に切り替えることもできはします。

[Project].Target.cs
        GlobalDefinitions.Add("USE_MALLOC_BINNED3=0");  // 追加 

3. 事前準備

計測前に以下の3つ手順を行います。

MALLOC_LEAKDETECTIONを有効にするためにTarget.csに以下の定義を追加

[Project].Target.cs
        GlobalDefinitions.Add("MALLOC_LEAKDETECTION=1");    // 追加 

② Debug Symbolを含めるためProject SettingsのbIncludeDebugFile=TRUEに変更
③ Developmentでパッケージを作成する

4. キャプチャ方法

 メモリのトラッキングを行ってレポートを出力する方法は2つありますが以下のコンソールコマンドを利用します。

手動出力の場合

mallocleak.start    // トレース開始
mallocleak.report   // リークレポート取得
mallocleak.stop     // トレース終了 

 mallocleak.startでトレースを開始してからmallocleak.stopでトレース終了するまでの間、mallocleak.reportを実行したタイミングでリークのレポートを出力します。レポートを取得したタイミングでmemreportとリークをトレースした.txtを出力します。

自動出力の場合

mallocleak.start report=n size=m    // トレース開始
mallocleak.stop                     // トレース終了 

こちらは上記と殆ど同じですが、memreportの取得周期(s)とフィルタサイズ(KB)を指定します。自動出力にすると以下のように定期的にmemreportの結果を出力します。

5. リークの解析

 もしリークが見つからない場合は、コンソールのログに以下のような出力を行います。その時にmemreportも出力されます。

リークなし
LogLeakDetector: No leaks found
LogUObjectHash: Compacting FUObjectHashTables data took   X.XXms
LogEngine: MemReportDeferred: saving to ../../../MyProject/Saved/Profiling/MemReports/NewMap-WindowsNoEditor-XX.XX-XX.XX.XX/XXX_NewMap.memreport

 もしリークが見つかった場合は、以下のようにメモリが確保された経路を出力します。デバッグシンボルを含めていない場合はコールスタックも表示されないので、利用の際には含めておく必要があります。

リークあり
AllocSize: 1776 KB, Num: 37, FirstFrame 1572, LastFrame 1890, KnownDeleter: 1, KnownTrimmer: 0, Alloc Rate 0.00B/frame
0x00007ff6546ae3db MyProject.exe!FWindowsPlatformStackWalk::CaptureStackBackTrace() [g:\github\unrealengine-4.24\engine\source\runtime\core\private\windows\windowsplatformstackwalk.cpp:363]
0x00007ff654316380 MyProject.exe!FMallocLeakDetection::Malloc() [g:\github\unrealengine-4.24\engine\source\runtime\core\private\hal\mallocleakdetection.cpp:451]
0x00007ff6543209da MyProject.exe!FMallocLeakDetection::Realloc() [g:\github\unrealengine-4.24\engine\source\runtime\core\private\hal\mallocleakdetection.cpp:519]
0x00007ff654320f14 MyProject.exe!FMallocLeakDetectionProxy::Realloc() [g:\github\unrealengine-4.24\engine\source\runtime\core\private\hal\mallocleakdetectionproxy.h:55]
0x00007ff654386e50 MyProject.exe!FMallocPoisonProxy::Realloc() [g:\github\unrealengine-4.24\engine\source\runtime\core\public\hal\mallocpoisonproxy.h:64]
0x00007ff654386fdd MyProject.exe!FMemory::Realloc() [g:\github\unrealengine-4.24\engine\source\runtime\core\public\hal\fmemory.inl:56]
0x00007ff6569a2fb1 MyProject.exe!TInlineAllocator<4,TSizedDefaultAllocator<32> >::ForElementType<FSimpleElementVertex>::ResizeAllocation() [g:\github\unrealengine-4.24\engine\source\runtime\core\public\containers\containerallocationpolicies.h:553]
0x00007ff656982633 MyProject.exe!FBatchedElements::AddVertex() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\batchedelements.cpp:113]
0x00007ff65757bbd0 MyProject.exe!FCanvasTextItem::DrawStringInternal_RuntimeCache() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\userinterface\canvasitem.cpp:1276]
0x00007ff6575774bc MyProject.exe!FCanvasTextItemBase::Draw() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\userinterface\canvasitem.cpp:903]
0x00007ff65755e685 MyProject.exe!<lambda_196d723fa86b1308605a351ef8f3c4cb>::operator()() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\userinterface\console.cpp:1551]
0x00007ff6575ab519 MyProject.exe!UConsole::PostRender_InputLine() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\userinterface\console.cpp:1566]
0x00007ff6575a9e81 MyProject.exe!UConsole::PostRender_Console() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\userinterface\console.cpp:1350]
0x00007ff656cbb14b MyProject.exe!UGameViewportClient::Draw() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\gameviewportclient.cpp:1696]
0x00007ff65749ca69 MyProject.exe!FViewport::Draw() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\unrealclient.cpp:1575]
0x00007ff656c8be12 MyProject.exe!UGameEngine::RedrawViewports() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\gameengine.cpp:661]
0x00007ff656c9bee8 MyProject.exe!UGameEngine::Tick() [g:\github\unrealengine-4.24\engine\source\runtime\engine\private\gameengine.cpp:1789]
0x00007ff653c9f59b MyProject.exe!FEngineLoop::Tick() [g:\github\unrealengine-4.24\engine\source\runtime\launch\private\launchengineloop.cpp:4485]
0x00007ff653cb1cac MyProject.exe!GuardedMain() [g:\github\unrealengine-4.24\engine\source\runtime\launch\private\launch.cpp:173]
0x00007ff653cb1d6a MyProject.exe!GuardedMainWrapper() [g:\github\unrealengine-4.24\engine\source\runtime\launch\private\windows\launchwindows.cpp:134]
0x00007ff653cc21bf MyProject.exe!WinMain() [g:\github\unrealengine-4.24\engine\source\runtime\launch\private\windows\launchwindows.cpp:263]
0x00007ff6584f8f42 MyProject.exe!__scrt_common_main_seh() 
0x00007ffc9d2ace51 ntdll.dll!UnknownFunction []