[UE4]GCClusterとその動作を調査する方法


#はじめに

UE4はガベージコレクション(GC)機能を有しており定期的に使われていないオブジェクト(メモリ)を検索し解放しています。


[アンリアルでのオブジェクト処理@公式ドキュメント](https://docs.unrealengine.com/4.26/ja/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/Objects/Optimizations/#%E3%82%AC%E3%83%BC%E3%83%99%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)

バージョンを経るごとに改善が施され、最近の実装は時分割されてGCが行われ非常に高速になっています。ですが大規模なプロジェクトになるとオブジェクトの数の増加に伴い大きな負荷となり、処理落ちなどの原因になりえます。

そのGCの負荷を抑える仕組みの一つとして**GCCluster**という機能があります。
大雑把にいうとアセットをロードしたときの芋ずる式にロードされたオブジェクトを**GCに対してひとまとめ**にする機能です。
この機能は**デフォルトで有効**で、クックされた(パッケージを作成した)ビルドでのみこの機能が動作します。

![GCCluster.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464248/4ae17a18-cac6-9a05-1a2d-b6e7c55d6663.png)

このGCClusterを上手に調整してGCパフォーマンスを改善した事例がFF7REMAKEチーム様のプレゼンテーションで解説されておりますで、合わせてご参照いただけるとよろしいかと思います。

https://www.youtube.com/watch?v=naaDgfKKzFU


#GCClusterが問題になるケース
通常GCClusterは問題無く動作しGCの効率を高めてくれますが、あるオブジェクトが複数のGCRootから共有されていてさらに読み込み順序の兼ね合いによって本来あるべきではないクラスタを形成したり、**ハードリファレンスが想定外のつながりを持ってしまっている**場合に、GCClusterRootを開放したつもりなのにその子要素が解放されずに残ってしまう。といった問題が発生することがあります。

基本的には参照関係などを整理するなどの対処で正しくクラスタが生成されるように調整するのがもっとも望ましいのですが、構造上そのような対処が難しい場合は各オブジェクト毎にクラスターのルートになりえるかどうかを返す、*UObjectBaseUtility::CanBeClusterRoot()* やクラスターに含まれても良いかどうかを返す *UObjectBaseUtility::CanBeInCluster()* を利用して調整を行うことになります。

がまず最初に問題点を調査して構造を把握する必要があります。その方法について解説させていただきます。

#原因の調査方法

まずはGCClusterが本当に原因になっているかを調べるためにGCClusterを一時的に無効化し問題に変化があるかを調べてみてください。
プロジェクト設定から設定が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464248/433220f0-3bfd-be01-25af-2c87af259965.png)
これで問題が改善するようであればGCClusterが悪影響を及ぼしています。

##オブジェクトの参照関係の確認 (obj refs)
もしメモリに残っているオブジェクトがわかっているのであれば、まず **obj refs** コマンドをつかって参照を調べるのをお勧めします。

https://qiita.com/donbutsu17/items/dd9e00bee27d6868ed3d


```
コンソールコマンド記述例
obj refs Name=/Game/Mannequin/Character/Textures/T_UE4Logo_Mask.T_UE4Logo_Mask
```

実行すると以下の様な形で情報がログに出力されます。
(ClusterRoot)や(Clustered)といった出力に注目してください。

```
LogReferenceChain: (root) (standalone) World /Game/ThirdPersonBP/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap->PersistentLevel
LogReferenceChain:  Level /Game/ThirdPersonBP/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap:PersistentLevel::AddReferencedObjects(): PersistentLevel
LogReferenceChain:   ThirdPersonCharacter_C /Game/ThirdPersonBP/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap:PersistentLevel.ThirdPersonCharacter_167->Class
LogReferenceChain:    BlueprintGeneratedClass /Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C::AddReferencedObjects(): ThirdPersonCharacter_C
LogReferenceChain:     ThirdPersonCharacter_C /Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.Default__ThirdPersonCharacter_C::AddReferencedObjects()
LogReferenceChain:      SkeletalMeshComponent /Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.Default__ThirdPersonCharacter_C:CharacterMesh0->SkeletalMesh
LogReferenceChain:       (standalone) SkeletalMesh /Game/Mannequin/Character/Mesh/SK_Mannequin.SK_Mannequin->MaterialInterface
LogReferenceChain:        (standalone) (ClusterRoot) Material /Game/Mannequin/Character/Materials/M_Male_Body.M_Male_Body->ReferencedTextures
LogReferenceChain:         (standalone) (Clustered) Texture2D /Game/Mannequin/Character/Textures/T_UE4Logo_Mask.T_UE4Logo_Mask
```

このように正常なリファレンスが出力されているなら問題ないのですが、(Clustered)のオブジェクトだけが浮いている状態になってしまった場合に十分な情報が得られない場合があります。この場合は**gc.ListClusters**を使います。

##GC.ListClusters (引数無し)
引数無しで起動した場合、現状メモリ上にあるクラスタールートのリストを出力します。

```
> GC.ListClusters

 Material /Game/Mannequin/Character/Materials/M_Male_Body.M_Male_Body (Index: 15556), Size: 29, ReferencedClusters: 0
 Material /Engine/EngineSky/M_Sky_Panning_Clouds2.M_Sky_Panning_Clouds2 (Index: 15429), Size: 7, ReferencedClusters: 0
 ParticleSystem /Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem (Index: 15739), Size: 23, ReferencedClusters: 0
 Displayed 3 clusters
 Total number of clusters: 3
 Maximum cluster size: 29
 Average cluster size: 19
 Number of objects in GC clusters: 59
 Maximum number of custer-to-cluster references: 0
 Average number of custer-to-cluster references: 0
```

##GC.ListClusters Hierarchy
Hierarchyオプションを追加してコマンドを入力するとすべてのクラスタールートとその下にぶら下がっているオブジェクトも出力します。

```
> GC.ListClusters Hierarchy

 Material /Game/Mannequin/Character/Materials/M_Male_Body.M_Male_Body (Index: 15556), Size: 29, ReferencedClusters: 0
     [0000]: Package /Game/Mannequin/Character/Materials/M_Male_Body (Index: 15359)
     [0001]: Package /Game/Mannequin/Character/Textures/T_UE4Logo_Mask (Index: 15460)
     [0002]: Package /Game/Mannequin/Character/Textures/T_Male_N (Index: 15461)
     [0003]: Package /Game/Mannequin/Character/Textures/T_Male_Mask (Index: 15462)
略
 Material /Engine/EngineSky/M_Sky_Panning_Clouds2.M_Sky_Panning_Clouds2 (Index: 15429), Size: 7, ReferencedClusters: 0
     [0000]: Package /Engine/EngineSky/M_Sky_Panning_Clouds2 (Index: 15317)
     [0001]: Package /Engine/EngineSky/T_Sky_Blue (Index: 15318)
     [0002]: Package /Engine/EngineSky/T_Sky_Clouds_M (Index: 15319)
略
 ParticleSystem /Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem (Index: 15739), Size: 23, ReferencedClusters: 0
     [0000]: Package /Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem (Index: 15701)
     [0001]: Package /Engine/Tutorial/SubEditors/TutorialAssets/M_smoke_subUV_blackbody (Index: 15707)
     [0002]: Package /Engine/Tutorial/SubEditors/TutorialAssets/T_soft_smoke (Index: 15708)
略
```

##GC.ListClusters With=[オブジェクト名] Hierarchy
オブジェクト名がわかっている場合、With=オプションにその名前をふくめることで属するクラスタだけをログに出力することができます。

```
> gc.ListClusters With=/Game/Mannequin/Character/Textures/T_Male_N.T_Male_N Hierarchy

Material /Game/Mannequin/Character/Materials/M_Male_Body.M_Male_Body (Index: 15556), Size: 29, ReferencedClusters: 0
     [0000]: Package /Game/Mannequin/Character/Materials/M_Male_Body (Index: 15359)
     [0001]: Package /Game/Mannequin/Character/Textures/T_UE4Logo_Mask (Index: 15460)
...
     [0023]: Texture2D /Game/Mannequin/Character/Textures/T_Male_N.T_Male_N (Index: 15550)
     [0024]: Texture2D /Game/Mannequin/Character/Textures/T_Male_Mask.T_Male_Mask (Index: 15551)
     [0025]: Texture2D /Game/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.T_ML_Rubber_Blue_01_N (Index: 15552)
     [0026]: Texture2D /Game/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.T_ML_Rubber_Blue_01_D (Index: 15553)
     [0027]: Texture2D /Game/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.T_ML_Aluminum01_N (Index: 15554)
     [0028]: Texture2D /Game/Mannequin/Character/Textures/T_UE4Logo_Mask.T_UE4Logo_Mask (Index: 15555)
   Referenced clusters: 0
   External (mutable) objects: 0
 Displayed 1 clusters
 Total number of clusters: 3
 Maximum cluster size: 29
 Average cluster size: 19
 Number of objects in GC clusters: 59
 Maximum number of custer-to-cluster references: 0
 Average number of custer-to-cluster references: 0
```

#リファレンス

UObjectClusters.cpp の *ListClusters()* を参照してください。