Unityゲームのメモリ分布概要

メモリはゲームパフォーマンス最適化の非常に重要な側面です。特にモバイルデバイスの場合、ハードウェアデバイスが限られているが、該当ユーザーを配慮する必要があります。ゲームはPCまたはMacで開発されますが、最終的には（ほとんど）モバイルで実行されます（AndroidとiOSのみが考慮されます）。メモリが適切に制御されていない場合、OOMの原因でモバイルのOSによってプロセスを強制終了することが起こしやすくなります。


しかし実際には、異なるオペレーティングシステムでは、メモリ管理戦略はまったく異なります。プラットフォームごとに専門的なメモリ分析ツールがありますが、これらのツールは、プラットフォーム、統計戦略、さらにはシステムバージョンが異なるために統計的な偏差を引き起こします。たとえば、XCodeのMemory Reportページは、Instrumentの下の独自の統計とは異なります。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/04acc9bf-7520-3a7d-89ea-ddd6a923cb1b.png)


テクスチャなどのいくつかのwrapのラッピング損失もあり、そのDataメモリのほとんどはNativeに入りますが、ロジックレイヤー呼び出しのためにいくつかのClassをラップする必要があり、この部分はMonoヒープに入ります。また、System FrameworkのConstデータの一部がCleanメモリに入り、別の部分がDirtyに入ります。これは、システムによってSwapメモリ​​にさらに圧縮される場合があります。

 

## 一、Unityの観点からのメモリ
Unityの観点からのメモリ分析は、Unity自体によって管理されるメモリに焦点を当てています（実際には、Unityによって割り当てられておらず、管理できません）が、Unityゲームは実際にはプラットフォーム（AndroidやiOSなど）で実行する必要があります）。）。そして、Unity自体によって割り当てられたメモリの以外に、システムの共有ライブラリからの部分もあります。さらに、複雑なUnityゲームは多くのサードパーティプラグインを参照することが多く、これらのプラグインによって割り当てられるネイティブメモリはUnityの考慮を超えています。

 

### 1.1 Unityのメモリの起源
Unityは、実際には、.netのスクリプト仮想マシンを使用するC++を使用して開発するゲームエンジンと見なすことができます。Unityは、仮想メモリからネイティブ（C ++）オブジェクトと仮想マシンにメモリを割り当てます。同様に、サードパーティプラグインのメモリ割り当ても仮想メモリプールにあります。

 

ネイティブメモリは、モノヒープを含むすべての必要なオブジェクトにメモリページを割り当てるために使用される仮想メモリの一部です。

 

前述のように、モノヒープは、仮想マシンのニーズに合わせて特別に割り当てられたネイティブメモリの一部です。これには、C＃によって割り当てられたすべての種類の管理対象メモリが含まれ、これらのメモリの管理対象は、ガベージコレクター（略してGC）です。

 

Unity内には、仮想メモリの短期および長期の割り当てニーズの管理を担当する特殊なアロケータがいくつかあります。すべてのUnityアセット（Assets）はネイティブメモリに保存されます。ただし、これらのアセットは、論理アクセスと呼び出しのためにClassに軽くパッケージ化されます。つまり、C＃でTextureを作成すると、その生データ（RawData）のほとんどがNativeメモリに存在し、小さなClassオブジェクトが仮想マシン（つまり、モノヒープ）に入ります。

 

### 1.2 Reserved/Used
メモリページング（iOSでは主に16K /page）は、メモリ管理の最小単位です。Unityがメモリを申請する必要がある場合、block（数ページ）で申請します。いくつかのGC（iOSの場合は8）を実行してもページがまだEmptyの場合、ページは解放され、実際の物理メモリが物理メモリに戻されます。

 

ただし、Unityは仮想メモリ内でメモリを管理しているため、仮想空間のアドレスは返されないため、「Reserved」になります。つまり、これらのReservedアドレススペースをUnityで繰り返し割り当てることはできなくなります。

 

したがって、32ビットオペレーティングシステムでは、仮想メモリアドレスが頻繁に割り当てられて解放されると、アドレススペースが使い果たされ、システムがそれを強制終了します。

 

### 1.3 GCとメモリの断片化
Monoヒープによって申請される物理メモリは連続しており、Monoヒープがオペレーティングシステムに拡張メモリを要求するのは非常に時間がかかります。したがって、ほとんどの場合、Monoヒープは、後の使用のため、申請された物理メモリを保持しようとします。したがって、仮想空間アドレス以外に、Monoヒープによって申請されるメモリにもReservedの概念があります。

 

メモリのアロケーションユニットはページであるため、つまり、ページにint値のみが格納されている場合、ページは引き続きUsedとして表され、物理メモリは解放されません。もちろん、特定のメモリが1ページより大きい場合は、複数の連続したページが申請されます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b67a6206-1863-6402-447f-04afb79339a4.png)


ある時点でヒープメモリがGCの場合、この部分の物理アドレスは空になります。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e97f0698-76a2-3b80-e3a4-154820f1b83e.png)


次回ヒープメモリを申請する必要がある場合、Monoヒープはまず、このメモリ申請を収容するために、現在のヒープに連続スペースがあるかどうかを確認します。十分でない場合は、GCが実行されます。そうです、最も嫌なGC操作です。その後、そのようなblockが見つからない場合、Monoヒープはメモリ拡張操作を実行し、オペレーティングシステムに追加のメモリを要求します。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/bccae48d-38be-eb28-5f48-bee59e2ce938.png)


そして、これらは空になりますが、再利用できないメモリはメモリの断片化になります。それらは使用も破壊もできません。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/ae59a7fa-8de3-7f76-2f89-58ab892e817e.png)
 

たとえば、上図では、Mono Reserved/Usedの関係は次のとおりです。

Reserved size：256KB + 256KB + 128KB = 640KB

Used：88 562B

 

### 1.4 Profiler Simpleビュー
UnityのProfilerをメモリ分析に使用する場合、Simpleモードでは、次のようなスクリーンショットが表示されます。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/04ed8561-ce81-e5c8-6442-23b952e7ed89.png)


ここに示されているのは、Unity自体によって管理される仮想メモリです。

 

これは明らかで、1行目はUsedメモリを示し、2行目はReservedを示しています。

 

- Total:
- Unity：Unityによって申請および管理されるすべてのメモリから<Profiler>、<FMOD>、および<Video>を差し引いたもの。つまり、Mono Heapが含まれます。
- Mono：ヒープメモリをホストする。
- GfxDriver：GPUメモリのオーバーヘッド。主にTexture、Vertex bufferおよびindex bufferで構成されます。ただし、Render Targetsは含まれていません。（他のプラットフォームのドライバーレイヤーも含まれません）
- FMOD：FMODによって申請されたメモリ。
- Video：ビデオファイルの再生に必要なメモリ。
- Profiler：プロファイラー自体のオーバーヘッド。
ここでTotal Reservedも、ゲームの仮想メモリの正確な値ではありません。理由は次のとおりです。

 

- ゲームのバイナリ実行可能ファイル、ロードされたlibraries、frameworksのサイズは含まれていません。
- GfxDriverの値には、レンダリングターゲットと、ドライバーレイヤーによって割り当てられたさまざまなバッファーは含まれていません。
- プロファイラーは、Unityコードによって行われた割り当てのみを表示し、サードパーティのnativeプラグインやOSの割り当ては表示しません。
- 
### 1.5 Profiler Detailedの詳細図
Detailedビューのサンプルは次のとおりです。

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b1cd5cd5-318b-d4ac-dbc6-84be4e0151e8.png)


仮想メモリの詳細な割り当てを示します。

 
- Assets――scenes、ResourcesとAsset Bundlesから現在読み込まれているアセットの合計。
- Built-in Resources――Unity EditorアセットまたはUnity defaultアセット。
- Not Saved――DontSaveとしてマークされたGameObjects。
- Scene Memory――GameObjectとそれに付属するComponents。
- Other—上記のカテゴリに含まれないその他。


ほとんどの場合、メモリの問題はAssetsで見つけることができます。たとえば、参照の数によってテクスチャとアセットのリークを見つけます（通常、リークされたアセットには参照の数がありません）。

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/503e872d-4f69-5f0a-240b-96d6be8a102f.png)

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/22261f14-aa0e-c793-8ef2-1a706119ccc7.png)



ここでは、OtherディレクトリのObjectsアイテムに注目したいと思います。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/702c66e1-fbfa-fa75-2be3-846765b5d7eb.png)


実際、この値はいくつかのBUGが原因です。このアイテムは、テクスチャやMeshなど、Objectから継承されたさまざまなオブジェクトを表します。それらはある時点で実際のオブジェクトから切断され、無視することができます。

 

- System.ExecutableAndDlls：これはUnityの推測です。ファイルサイズを合計することにより、ロードされたバイナリによって使用されたメモリを推測しようとします。
- ShaderLab：これらはシェーダーのコンパイルに関連する割り当てです。シェーダー自体には独自のobject rootがあり、シェーダーの下に一覧表示されます。
### 1.6 Unityの視点の制限
Unityのメモリ分析は、ネイティブProfilerという項目があるだけではなく、よく使用されることは以下のように示しています。

- MemoryProfiler
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/dae1e09e-0148-deb5-6786-dfb47089cd1e.png)


- MemoryProfiler Extension

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/26238018-c7ae-4d7f-6129-6ac20d995c72.png)

 

しかし、それらはすべて同じ問題を抱えています。つまり、Unity自体が提供するProfilerAPIに依存しています。すなわち、ツールはデータの表示方法と操作方法が異なりますが、測定した結果が同じです。

 

つまり、Unityの観点から見たツールは、Unityコードによって行われた割り当てのみを認識し、サードパーティのnativeプラグインとオペレーティングシステムの割り当ては認識しません。

 

ただし、完全なUnityプロジェクトは最終的にプラットフォーム上で実行され、プラットフォームのメモリ分析ツールの統計結果に大きなエラーが発生します。さらに、Unityプロジェクトの実際のメモリ分散とオーバーヘッドを把握することは困難です。

 

この記事では、iOSプラットフォームでのUnityゲームのメモリ状況を紹介します。

 

## 二、XCodeの観点から見たメモリ
Unity独自のツールがメモリ分散のパノラマ統計を満たすことができない場合は、最高のデバッグ機能を備えたXCodeツールを使用します。通常、UnityプロジェクトをXCodeプロジェクトにエクスポートしてから、XCodeとそのInstrumentforProfilerを使用する必要があります。

 

### 2.1 iOSの観点からのメモリ管理
依然として起源から話します。iOSの観点からのメモリは、Unityの観点からは完全に異なります。概念的、管理的、またはカテゴリ的にも。

 

オペレーティングシステムとして、iOSはUnityほど詳細ではなく（実際にはそれができません）、オペレーティングシステムレベルでのメモリ管理とサンドボックスAPPレコードのさまざまな基盤となるメモリ操作に関心がありますが、これもスタックからのみ反映できます。実際、オペレーティングシステムからメモリを要求するAPPの記録を記録します。

 

UnityプロジェクトはiOSプラットフォーム上でAPPとして実行され、iOSシステムに通常のAPPとしてのみ記録および分析されます。実際、Unityにはネイティブアプリと比較して自然な欠点があります。iOSのネイティブAPPやコントロールと比較すると、Unityはメモリを申請する目的を認識していません。つまり、オペレーティングシステムは、APPによって申請されるメモリがどのように使用されるかにまったく注意を払いません。

 

これは、親（iOS）が子供（Unity）にポケットマネーを渡すようなものです。親は、一昨日、子供が100円を要求し、試験紙を購入したいと言ったと記録します（スタックレコード）。昨日、またクラス費用を出すと50円を要求し、今日は、クラスメートと食事するとさらに100円を要求しました。子供が要求しすぎて親の許容限度を超えた場合、それは終了します（今月のポケットマネーはありません！）。

 

子供の見方が違います。昨日1000円、国語20円、数学20円、英語20円、往復車8円、残りは32円しかないなあ、まあ、親に返しなくてもいい、万が一また物理学や化学を買うなら、どうしますか。

 

### 2.2 iOSで使用されるメモリの種類
仮想メモリのみに注意を払うUnityとは異なり、OSはPhysical Memory（RAM）に注意を払う必要があります。特にモバイルプラットフォームでは、限られたメモリを極限まで使用する必要があります。したがって、多くのPCプラットフォームで使用されているメモリ戦略は、スワップスペースなどのモバイルプラットフォームでは使用できません（iOSはCleanタイプのメモリのPagingのみを実行できます）。

 

次に、iOSシステムで使用されるメモリタイプを一覧表示します。

 

**Physical Memory**：iOSデバイスの物理チップメモリ​​、よくマシンメモリと呼ばれています。モバイルデバイスでは、物理メモリの実際の使用量は、オペレーティングシステム自体の使用量から差し引かれます。iOSメモリクラッシュしきい値この記事では、さまざまなiOSデバイス上のアプリで使用できる物理メモリの量を記録します。

 

**Virtual Memory（VM）**：仮想メモリは、OSによって各APPに割り当てられる仮想アドレス空間でもあり、Unityの仮想空間アドレスに似ています。これは、メモリ管理ユニットMMU（memory management unit）によって管理され、実際の物理メモリにマップされます。前述のように、メモリはページごとに割り当てられます。初期のiOSプロセッサは4K /ページを使用します（一部の世代は64Kを使用します）。A9以降は、16K/ページが均一に使用されます。仮想メモリは通常、コードセグメント、ダイナミックライブラリ、GPUドライバー、Mallocヒープ、およびその他の部分で構成されます。

 

**GPU Driver Memory**：iOSシステムは、いわゆる統合アーキテクチャを使用します。つまり、GPUとCPUは、ドライバーによって割り当てられるテクスチャやメッシュデータなどのメモリの特定の部分を共有します。さらに、いくつかのGPUドライバーレイヤーとGPU専用メモリタイプ（Video memory）があります。

![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/450e9cf6-4bfd-f339-ac50-cf1327f610a6.png)


**Malloc Heap**：APPが実際にメモリを申請する場所。Unityのメモリ申請の動作はここで発生し、メモリアプリケーションはmallocおよびcalloc関数を介して実行されます。 Appleは、使用できる最大仮想ヒープのアドレスを公開していません。理論的には、ポインタのサイズ（32ビットまたは64ビット）によってのみ制限されますが、実際の経験は理論値よりはるかに低くなります。それに規則はありません。実際の経験は次のように要約されます。

![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a732ceda-c341-15a3-00c1-c93311d9d9b1.png)


Unity自身の仮想アドレスと同様に、アプリケーションがメモリを頻繁に要求したり解放したりしないことが最善です。

 

**Resident Memory**：常駐メモリ。ゲームまたはAppが実際に占有する物理メモリです。一般的に、アプリケーションがシステムにメモリを要求すると、仮想メモリは直接大きくなります。ただし、割り当てられたメモリがデータを書き込まない場合、実際の物理メモリ割り当ては生成されません。したがって、仮想メモリは>=常駐メモリです。

 

**Clean Memory**：Cleanメモリは常駐メモリの一部です。しかし、この部分のメモリタイプは読み取り専用です。一般的なCleanメモリには、System frameworksの定数部分、アプリケーションバイナリファイル、メモリマップトファイルなどが含まれます。読み取り専用であるため、アプリケーションのメモリが不足するとPage Outになる可能性があります。

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/844dc311-d427-50af-8e57-87fa11b0d4d4.png)

![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/19b9015c-3cfb-3fd6-ffcc-25c8fd961a27.png)



**Dirty Memory**：Cleanの反対はDirtyメモリです。この部分は、OSがページングできないものを指します。

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/83a53875-3b5e-1ded-7d46-7a62e68f63e8.png)


**Swapped Compressed Memory**：SwappedCompressedは実際にはDirtyメモリに属しています。アプリケーションメモリが不足している場合、OSはダーティメモリにある使用頻度の低いメモリを圧縮して保存します。使用する必要がある場合は、再度解凍してください。これらのアルゴリズムと戦略は現在公開されていませんが、経験から、OSは積極的に圧縮を行ってダーティメモリの総量を減らします。ここでの圧縮スワップは、従来のオペレーティングシステムのディスクスペーススワップではないことに注意してください。

![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/cc2b265a-c5a0-827d-917e-9680e1ef6ea5.png)

![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b7cf7df7-341d-83f9-0673-c72399626d7a.png)



上の図は、圧縮プロセスを示しています。圧縮と解凍はどちらもCPUに負荷がかかります。

 

### 2.3 FootPrint
FootPrintは、アップルが推奨するメモリ測定および最適化の指標です。Memory Footprintの値がLimit lineに達すると、メモリー警告がトリガーされ、さらにOOMにつながります。

 

Footprintは主にDirtyとCompressedで構成されています。または、ResidentはFootprintとCleanで構成されています。Footprintには統一された基準はありません。デバイス、オペレーティングシステム、さらには現在のランダム環境によっても異なります。

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3c8bd47d-bb94-f627-c1cc-f008028b9725.png)


Byteの既存のテストツールGamePrefは、iOSをテストするときにFootprintメモリを取得します。詳細なデータについては、GamePerfユーザーマニュアルを参照してください。

 

### 2.4 Xcodeメモリゲージ
これは、XCodeでデバッグするときに最も簡単なインターフェイスです。 「Debug」タブに切り替えると表示されます。

![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e593c6cd-8758-e868-d163-423532df46c0.png)


緑はメモリが良好であることを示し、黄色は危険な領域を表示し、即時に処理しないと、OSによってすぐに強制終了されます。

 

メーターの最大メモリはデバイスの物理メモリを指しますが、実際には、正式な場合でポインタメモリがどのようなメモリを指すかを説明していません。ただし、テスト結果から、VM Trackerツールで測定されたDirty Memory + Swapped Memoryよりも常に10〜15MB大きくなります。

 

しかし実際には、OSがAPPを強制終了するための基準はこの値だけではありません。通常、アプリを強制終了する場合は、次の手順を実行します。

 

- Cleanメモリページを削除してみてください。
- APPがDirtyメモリを大量に使用すると、OSはメモリ警告を送信してリソースを解放させます。
- いくつかの警告の後、Dirty使用量がまだ高い場合、強制終了されます。
iOSによるAPPの強制終了戦略も不透明であるため、APPがシステムによって終了されるのを防ぐ唯一の方法は、Dirtyメモリを可能な限り減らすことです。

 

### 2.5 VMトラッカー
VM Trackerは、XCodeInstrumentsツールセットの1つです。より詳細な仮想メモリの分散を提供し、Dirty Memory情報を提供するためのツールでもあります。ただし、残念ながら、メモリ割り当ての目的とタイミングは示されていません。

 

典型的なVM TrackerのProfilerスナップショットは次のとおりです。

 ![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/35e9a4b4-bafe-7d12-fe51-5894c95069c8.png)




ヘッダーは次のとおりです。

**Type**  — メモリタイプ

**Resident Size**  — Resident Memoryメモリ

**Dirty Size**  — Dirty Memoryメモリ

**Swapped Size**  — Compressed Swapped Memoryメモリ

**Virtual Size**  — Virtual Memoryメモリ

**Res.%**  — Resident Memory和Virtual Memoryの比率

 

次は、さまざまなレベルでのいくつかの実際のタイプの特定の値です。タイプを1つずつ紹介することはしませんが、いくつかの重要なポイントを取り上げて説明します。

 

**All**—すべての割り当て

***Dirty***— Dirty Memory

**IOKit** — graphics driver memory、テクスチャ、メッシュ、コンパイルされたシェーダーなどのグラフィックドライバメモリ。

**VM_ALLOCATE** — 主にMono Heap。この値が大きすぎる場合は、Unity Profilerを使用して特定の割り当てを表示できます

**MALLOC_***—主にUnity nativeまたはthird-party pluginsよって割り当てられたメモリ

**__TEXT** —読み取り専用の実行可能コードセグメントと静的データ

**__DATA** —書き込み可能な実行可能code/data

**__LINKEDIT** —シンボル、string、再配布テーブルなど、さまざまなリンクライブラリの実際のメタデータ。

 

仮想メモリやダーティメモリなどの各グループの情報を比較することで、メモリの根本原因分析を行うことができます。たとえば、上記のスナップショットでは、分析からどのような結論を導き出すことができますか？

 

#### 2.5.1Regions Map
Regions MapはVM Trackerの別の視点であり、主にメモリページングの表示と仮想アドレス空間の構造を提供します。

![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/c1d24ff1-3027-2a3a-6928-56ae36d259c3.png)


たとえば、上の図からわかるように、MonoHeapブロックのメモリは連続していません。

 

### 2.6 Allocations
Allocationsツールは、すべてのスレッド（Unityネイティブコード、ガベージコレクター、IL2CPP仮想マシン、サードパーティプラグインなど）によってすべてのスレッドで行われた、アプリケーションのアドレス空間内のすべての割り当てを表示します。

 

VM Trackerと比較すると、Allocationsの利点は、任意の期間のメモリ割り当てを表示できることです。スタックは、どのコードが割り当てを行ったかを確認することもできます。

 

しかし、それはまだバグがあり、割り当ての状況を確認できますがことが、常駐の状況を確認することができません。

 

CallTreeの観点から、スタックを見ることができます。

![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/df4cef9a-efb2-19b0-c2c5-bf26cc9cd764.png)


たとえば、上のスクリーンショットからわかるように、コードはいくつかのPoolerのクローンを作成するPoolerクラスのインスタンスを作成し、その結果、メモリが割り当てられます。

 

Summaryの下に、仮想マシンのメモリ割り当ての詳細なリストが表示されます。

![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/127e22e6-c8cb-b74d-04ff-3a7a02ec4dbf.png)


クリックを選択すると、より詳細な割り当てが表示されます。たとえば、次のメモリ割り当てはJsonの解析によって発生します。
![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/826c58c1-2e59-6b6a-5528-0318778d5c9f.png)

 

### 2.7 Memory Debugger
ツール分析に加えて、XcodeはMemory Debugger機能も提供します。プロジェクト設定でMallocStackを開く必要があります。

![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/f06a6f35-302d-23df-50ef-8f4a15698ee1.png)


次に、Debugタブの下のロゴをクリックして、メモリフレームをキャプチャして分析できます。

![28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/ea34729b-7161-2dc1-750d-8239c0171b16.png)
![29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/ef4c7ba8-6339-fe6a-d318-7594c26c4b53.png)


ここでは、各バイトの割り当てを表示できます。しかし、それはあまりにも詳細です。

 

### 2.8 vmmap
Memoryスナップショットをエクスポートすることで、コマンドラインツールを使用してメモリをより詳細に分析することもできます。

![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/87981f46-90b7-c974-dc41-559df08a83b7.png)


エクスポートされたmemgraphファイルは、vmmapのコマンドラインを介して物理メモリの実際の割り当てを表示できます。

![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a163ed36-f5fc-1c38-d91e-1bf877ae09b0.png)

![32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/9ba1ef50-1972-f34c-7c15-4ea9bfc84503.png)

もちろん、vmmapツールには、より多くの詳細とコンテンツの表示をサポートするためのより多くのコマンドラインがあります。興味がある場合は、Jiadong氏が書いた[「Unity開発者向けiOSメモリデバッグガイド」(中国語注意）](https://zhuanlan.zhihu.com/p/87310853)の後半を確認してください。

![33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/0f40c232-9c78-cc8e-e179-e4404a876dc9.png)


メモリリークを分析する場合は、leaksApp.memgraphコマンドを使用することもできます。たとえば、次の循環を参照：

![34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/062f0aac-9f53-bc8f-d9b5-a36c2a18f19e.png)


### 2.9 Xcode視点の制限
XCode視点にも独自の制限があります。 Unityの主な焦点または管理の重点は、仮想メモリです。また、XCodeは、OSの観点から物理メモリに重点を置いています。

 

Unityはシステムライブラリとサードパーティのプラグインをカウントできません。XCodeはカウントできますが、区別するのは困難です。 OSの場合、これらのメモリはすべてAPPによって要求され、すべてMalloc Heapの申請であるため、すべて1つのカテゴリに分類されます。本当にライブラリを区別したい場合は、すべての関数割り当てを手動で収集してから、Unity、APlugin、Luaの割り当てなどを手動で分類する必要があります。

 

もう1つは、XCodeには多くの分析ツールがありますが、これらのツールには、それぞれの統計的次元の追加に関して、依然として異なる問題があります。とはいえ、XCode独自のツールを使用しても、標準を完全に統一することはできません。

 

最後に、XCodeのツールチェーンの統計は低レベルで詳細すぎます。これを使用してメモリの例外をすばやく特定できますが、すべてのメモリをすばやく分類することは困難です。

 

## 三、Androidの観点から見たメモリ
iOSオペレーティングシステムはUnixに基づいており、AndroidオペレーティングシステムはLinuxに基づいており、LinuxはUnixに基づいているため、カーネルの面から見ればAndroidオペレーティングシステムはiOSと非常に似ています。

 

したがって、Androidのメモリ管理戦略はiOSと非常に似ています。ただし、違いは、iOSシステムが閉じられており、各世代のハードウェアが既知であり、列挙可能でさえあることです。 Androidシステムは、オープンソースであるため、さまざまなハードウェアを備えており、制御が非常に困難ですが、オープンソースシステムによってさらに可能になります。

 

### 3.1 Androidの視点からのメモリ管理
メモリポリシーは似ていますが、用語と実際の管理プロセスにはわずかな違いがあります。たとえば、Androidはメモリを次の3つのタイプに分類します。

 

**RAM**：通常、メモリとも呼ばれますが、そのサイズは制限されています。ハイエンドデバイスは通常、RAM容量が大きくなります。

**zRAM**：スワップスペースに使用されるRAMパーティションです。メモリが不足している場合、OSはデータの一部をRAMに圧縮し、zRAMに保存します。デバイスメーカーは、zRAMのサイズに上限を設定できます。

**Storage**：一般にストレージと呼ばれます。通常のAPP、写真、キャッシュファイルはすべてここにあります。

 

ただし、iOSのFootprintとは異なり、Androidのメモリは別の名前です。

**VSS**―Virtual Set Sizeの仮想使用メモリ（共有ライブラリが占有するメモリを含む）

**RSS**―Resident Set Sizeに実際に使用されている物理メモリ（共有ライブラリによって占有されているメモリを含む）

**PSS**―Proportional Set Sizeによって実際に使用される物理メモリ（プロポーショナル割り当ての共有ライブラリによって占有されるメモリ）

**USS**―Unique Set Sizeプロセスのみが占有する物理メモリ（共有ライブラリが占有するメモリを除く）

 

一般的に、メモリ使用量のサイズは次のとおりです。VSS> = RSS> = PSS>=USS。

 

 

現在、Android上のUnityのゲームの指標はデフォルトでPSSを使用しています。どういう意味ですか？

 

たとえば、次のようなメモリページがあります。

![36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/f8df0aff-9845-135b-135a-881865b1f40c.png)


それらの1つは、GooglePlayと特定のゲームアプリの両方で使用される位置共有サービスです。これにより、どちらのAPPがより多くを使用するかを定めるのが難しくなります。ロケーション共有サービスのすべてのメモリを2つのアプリケーションに追加すると、コンピューティングの観点はRSSになります。これは確かにより正確な物理メモリ使用量ですが、この方法で、ロケーション共有サービスは2回計算されるようになり、3つのアプリケーションに対しては3回になります。明らかに、これは正しくありません。

 

では、一応、皆はこの共有サービスメモリを等分すれば？この計算の観点ならPSSです。完全に合理的ではありませんが、現時点で最もバランスの取れたソリューションと言えます。

![37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/4a1ea2fd-3029-f5a8-c8f4-20fe2f44cb06.png)


### 3.2 LMK（Low Memory Killer）
iOSでは、Footprintは閾値に達するとOSによって強制終了されます。これは、Androidにも当てはまります。ただし、iOSと比較すると、AndroidのLMKプロセスはより透過的です。

 

LMKは、oom_adj_scoreと呼ばれる「メモリ不足」スコアを使用して、実行中のプロセスに優先順位を付け、これを使用して、強制終了するプロセスを決定します。スコアが最も高いプロセスが最初に終了します。バックグラウンドアプリケーションが最初に終了し、システムプロセスが最後に終了します。次の表に、LMKスコアのカテゴリを高から低まで示します。スコアが最も高いカテゴリ、つまり最初の行のアイテムが最初に終了します。

![38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a3ce49b5-b10d-d066-e1d1-b46a778b45c4.png)


以下は、上記の表のさまざまなカテゴリの説明です。

**Background apps**：以前に実行されていて、現在アクティブではないアプリ。 LMKは、oom_adj_scoreが最も高いアプリから始めて、最初にバックグラウンドアプリを強制終了します。

**Previous app**：最近使用されたバックグラウンドアプリ。ユーザーはバックグラウンドアプリよりも前のアプリに切り替える可能性が高いため、前のアプリの優先度はバックグラウンドアプリよりも高くなります（スコアが低くなります）。

**Home app**：ランチャーアプリ。アプリを終了すると、壁紙が消えます。

**Services**：アプリによって開始されるサービス。同期やクラウドへのアップロードが含まれる場合があります。

**Perceptible apps**：小さなインターフェースを表示する検索プロセスの実行や音楽の再生など、ユーザーが何らかの方法で知覚できる非フォアグラウンドアプリケーション。

**Foreground app**：現在使用中のアプリ。フォアグラウンドアプリを終了すると、アプリがクラッシュしたように見え、デバイスに問題があることをユーザーに警告する場合があります。

**Persistent**：これらは、テレフォニーやWLANなどのデバイスのコアサービスです。

**System**：システムプロセス。これらのプロセスが強制終了された後、携帯は再起動する場合があります。

**Native**：システムで使用される非常に低レベルのプロセス（例：kswapd）。

 

デバイスメーカーは、LMKの動作を変更できます。

 

### 3.3 Android Profiler
AndroidプラットフォームでUnityパッケージを構築するのに便利であり、Android独自のStudioにはメモリとパフォーマンス分析ツールがないため、多くの場合、AndroidがUnity Profilerに接続する後、メモリデバッグを実行することを選択します。

 

しかし実際には、Androidにはパフォーマンスを分析できるツールがたくさんあります。例えば：

https://developer.android.com/studio/profile

 

ほとんどの場合、XCodeは引き続き分析に使用されるため、この部分は現在実行されていませんので、将来的には、ツールの使用法とスキルを詳細に調査する時間を取ります。

 

さらに、Googleの会議で、Android開発者は最新のパフォーマンス分析ツールPerfettoの使用を推奨しました。

https://perfetto.dev/docs/quickstart/android-tracing

 

今のところ、AndroidのツールはXCodeと同じく、アプリケーションでのメモリ割り当てがUnityによって行われるのかPluginによって行われるのかを区別する方法がありません。だから彼らの意見も分離測定を実行することです。

 

## 四、予想される選択の方向
上記の研究結果を考慮して、2つの解決策が検討されました。

 

### 4.1 1つずつ解決する
Unityの観点から考えます。Unityはサードパーティのプラグインの使用量をカウントできないため、「差異法」を使用して、使用されていた各サードパーティのプラグインを1つずつ解決します。

 

たとえば、miniプロジェクトに基づいて、まずはminiプロジェクトの現在のメモリ指標値を測定します。そして、サードパーティのプラグインに接続し、ユースケースを使用して同じインジケーターを再度テストします。結果として生じる差は、プラグインのメモリ使用量とほぼ同じです。

 

この方法ですべてのプラグインの指標を測定する上に、Unity独自のReservedメモリを加え、現在のメモリ分布と見なすことができます。

 

この方法には当然欠点があります。

 

差異値は、異なるデバイスの現在の環境の共有ライブラリの影響を受けます。これにより、Pluginが異なるマシンでまったく異なる可能性があります。

 

実にどの客観的条件または内部ロジックの影響を受けるかは特定できません。

 

モバイルプラットフォームのダーティメモリポリシーにより、実際に測定できるのは仮想メモリの相違だけです。詳細については、次のテストの結論を参照してください。異なるメモリの割り当て方法は実際のメモリに対する影響。

 

変数を制御することは困難です。したがって、プラグインを十分にカバーできるテストケースを作成する必要があります。

 

### 4.2 海から月を釣る
もう一つの方法は、海から月を釣ることです。 最下層のMallocから始めて、Hook関数を記述してメモリ申請を監視し、最後に要約して分析します。たとえば、この記事：[『mallochookメモリ割り当てコールバック（glibc-3-memory）』(中国語注意)](https://www.jianshu.com/p/2beb412bd97b。)

 

これは、実際にはモバイルプラットフォームのメモリルールモデルと同じです。カスタムの方法でツールの表示ルールをさらにカスタマイズできるということだけが違っています。解決策は実行可能ですが、モバイルプラットフォームツールと同じ問題があり、統計されたスタックをどのように分類すればよいか。どの関数がエンジンに属し、どの関数がサードパーティのプラグインに属し、またはシステム共有ライブラリまたはフレームワークに属しているかを判断するにはどうすればよいですか？

 

Androidメモリについて：

[https://zhuanlan.zhihu.com/p/372883142](https://zhuanlan.zhihu.com/p/372883142)(中国語注意)

[https://zhuanlan.zhihu.com/p/151238164](https://zhuanlan.zhihu.com/p/151238164)(中国語注意)

 

**5.参考:**

[1] [Fixing Performance Problems – 2019.3 – Unity Learn](https://learn.unity.com/tutorial/fixing-performance-problems-2019-3)

[2] [Understanding iOS Memory](https://docs.google.com/document/d/1J5wbf0Q2KWEoerfFzVcwXk09dV_l1AXJHREIKUjnh3c/edit)

[3] [https://zhuanlan.zhihu.com/p/87310853](https://zhuanlan.zhihu.com/p/87310853)

[4] [Finding iOS memory | Rambling Llamas](https://liam.flookes.com/wp/2012/05/03/finding-ios-memory/)

[5] [Unity – Manual: Understanding the managed heap](https://docs.unity3d.com/Manual/performance-managed-memory.html)

[6] [Memory Management in Unity – Unity Learn](https://learn.unity.com/tutorial/memory-management-in-unity)

[7] [iOS Memory Deep Dive – WWDC 2018 – Videos – Apple Developer](https://developer.apple.com/videos/play/wwdc2018/416/)

[8] [Delivering Optimized Metal Apps and Games – WWDC 2019 – Videos – Apple Developer](https://developer.apple.com/videos/play/wwdc2019/606/)

[9] [Gain insights into your Metal app with Xcode 12 – WWDC 2020 – Videos – Apple Developer](https://developer.apple.com/videos/play/wwdc2020/10605/)

[10] [https://developer.android.com/topic/performance/memory-management](https://developer.android.com/topic/performance/memory-management)

[11] [Memory allocation among processes](https://developer.android.com/topic/performance/memory-management#types_of_memory)

[12] [Understanding Android memory usage](https://www.youtube.com/watch?v=w7K0jio8afM)

[13] [Android memory and games](https://www.youtube.com/watch?v=Do7oYWwOXTk&t=314s)

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com