Xamarin Profiler と Android Profiler を併用して Xamarin Android アプリのメモリ使用量を測定する

Xamarin 製アプリのメモリのプロファイリングは、 

* .NET(mono ランタイム) が管理するオブジェクト
* Java(Dalvik) が管理するオブジェクト

を意識する必要があります。

## 使用するツール

.NET(mono ランタイム) が管理するオブジェクトのメモリ測定には、

* [Xamarin Profiler - Xamarin | Microsoft Docs](https://docs.microsoft.com/ja-jp/xamarin/tools/profiler/?tabs=vsmac)

を使用します。これは Visual Studio と連携するアプリで、IDE から Run
 > Start Profiling とすると起動できるものです。

Java(Dalvik) が管理するオブジェクトのメモリ測定には、

* [Android Profiler でアプリのパフォーマンスを測定する  |  Android Developers](https://developer.android.com/studio/profile/android-profiler?hl=ja)

を使用します。こちらは Android Studio をインストールすれば一緒に入っています。

## 試してみた

### Xamarin Profiler

Xamarin Android で、画面に2つのボタンを配置し、それぞれ次のような処理を行うコードを書きます。

```csharp
[Activity(Label = "LeakSample", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity
{
    System.IO.MemoryStream netStream = new System.IO.MemoryStream();
    Java.IO.ByteArrayOutputStream javaStream = new Java.IO.ByteArrayOutputStream();

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        FindViewById<Button>(Resource.Id.myButton1).Click += (_, __) =>
        {
            var size = 10 * 1024 * 1024;
            netStream.Write(new byte[size], 0, size);
        };

        FindViewById<Button>(Resource.Id.myButton2).Click += (_, __) =>
        {
            var size = 10 * 1024 * 1024;
            javaStream.Write(new byte[size]);
        };
    }
}
```

``myButton1`` を押したときには、 .NET のクラスである ``System.IO.MemoryStream`` にデータ追加します。

``myButton2`` を押したときには、 Java のクラスである ``Java.IO.ByteArrayOutputStream`` にデータ追加します。

こんなプログラムを Run > Start Profiling で起動してみます。
アプリが起動する前に Xamarin Profiler が起動します。Choose target は既に起動したい Android アプリが設定されているので、「割り当て」を選択して「Next」します。

次の画面の「Enable automatic snapshots」をチェックすると自動的(一定間隔で)にメモリのスナップショットを記録しますがこれはお好みで。

「プリファイリングの開始」を押すとアプリが起動します。
Xamarin Profiler の上部にある カメラアイコン を押すと任意のタイミングでメモリ状態を記録できます。

``myButton1`` を数回押してから カメラアイコン を押してみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/83830c8c-486f-e9da-5a1b-89b11e96c88f.png)

上図のように、追加した byte データが記録されています。

次に、``myButton2`` を数回押してから カメラアイコン を押してみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/a82fb1e1-f810-c511-ec10-260e941676eb.png)

今度は Dalvik 管轄のオブジェクトへデータを追加したので Xamarin Profiler ではそのクラスは観測できません。（ワーキングセットは増えてるのでそれは観測できる？）

### Android Profiler with Xamarin.Android apps

Dalvik が管理している領域のプロファイリングは Android Profiler を使います。
Xamarin であっても「それは Android/Java API をラップしただけ」の実体はネイティブ Android アプリなので、Android SDK のツールは使えるのです。

Android Studio を起動して適当な Android Apps のプロジェクトを開くか作ります（これはダミーです）。
次に メニュー > View > Tool Windows > Profiler で下部に Profiler ペインが開きます。
SESSIONS の横の「＋」を押して、対象の端末 > Other processes > 対象のアプリID を選択すると、プロファイリングが始まります。

この状態で、アプリの ``myButton2`` を何度か押すと、下図のようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/ac764a06-0947-a4e1-b579-75cde686d392.png)

ボタンを押すたびにメモリ使用量が増えているのが確認できます。
（実は ``myButton1`` を押すと、最初の1回はメモリ使用量が増えます。".NET だけ" には留まらないなにか、があるのでしょうか、おそらく。）

## まとめ

このように Xamarin(.NET)側のプロファイリングには Xamarin Profiler が、Java/Android API使用部のプロファイリングには Android Profiler がそれぞれ使用でき、同時利用あるいは併用することで、 Xamarin Android アプリの計測ができます。

これは Xamarin.Forms アプリでも同様です。その場合、Custom Renderer やライブラリの Android 依存な箇所の計測は Android Profiler に頼ることがでてくるでしょう。

また、Xamarin Profiler は Xamarin.iOS アプリの計測もできます。というか Xamarin Profiler の Look&Feel は Xcode - Instruments にとても似ていますね。

各種プロファイラ自体の使い方や機能については、私も全然把握できてないので、知見が溜まったらまた何か書きます。