3

私の iPad アプリは MonoTouch で開発されています。これは、メモリ管理の地獄をすべて回避したかったためですが、そうではないようです。シミュレーターではすべて正常に動作しますが、デバイスでアプリをテストしたところ、いくつかのメモリ警告の後、OS によってすぐに強制終了されることがわかりました。私のアプリは単純な画像ブラウザーです。いくつかの PNG 画像を読み込み、UIScrollView 内のいくつかの UIViews を使用してそれらを表示し、タッチすると次または前の画像を読み込みます。シミュレーターでは問題なく動作します。しかし、約 6 ~ 11 個のイメージをロードおよびアンロードした後のデバイスでは、メモリ警告が表示され始め、突然プロセスが強制終了されます。すべてのインスタンス化サイクルを確認し、新しいイメージをロードする前にすべての参照を正しく削除しました。

そこで、Instruments を起動し、iPad でアプリのメモリ割り当てのプロファイリングを開始しました。そこで私は、ライブ バイトが約 5 ~ 9 MB しかないことを発見しました。それはLive Bytesです)それは殺されています!これは、私のアプリの Instruments プロファイリング セッションのスクリーンショットです。

モノタッチでのメモリ割り当ての問題を記録した機器のスクリーンショット

ヒープショット シーケンスは次のとおりです。

楽器での私のアプリのヒープショット

小さな漏れもありますが、犯人になるほど大きくはないと思います。これらはすべて、iOS 5.1 で UIScrollView をリリースする際の既知の問題である strdup からの 48 バイトのリークです。

ここに画像の説明を入力

すべてが正常に見え、割り当てられたライブ バイトがまだ 5 MB であっても、私のアプリの REAL メモリは指数関数的に増加してから、iPad では最大 50 MB まで、iPhone4S では最大 314 MB まで、メモリ モニタによって報告されます。

iPhone4S でのアプリのメモリ増加

問題がどこにあるのかを発見するための方法やユーティリティがあるかどうか、誰かが教えてくれますか? モノタッチガベージコレクタのバグですか?または、正しく処分していないオブジェクトがありますか? そして、プロファイラーでそれらを見つけるにはどうすればよいですか? コードを 2 日間チェックしましたが、すべて問題ないようです。

ロード/インスタンス化/破棄サイクルのコードは次のとおりです。

    void StartImageLoadingThread()
{
    tokenSource = new CancellationTokenSource ();
    token = tokenSource.Token;
    Task task1 = new Task( () => PerformLoadImageTask(token),token);
    task1.Start();
}


void PerformLoadImageTask(CancellationToken token)
{
        if (token.IsCancellationRequested)
                {
                     Console.WriteLine("Task {0}: Cancelling", Task.CurrentId);
                return;
                }

            current_uiimage_A = UIImage.FromFileUncached(file_name);
            page_A_image_view.Image = current_uiimage_A;


} 


void UnloadImageAFromMemory()
{
      page_A_image_view.Image = null;
      current_uiimage_A.Dispose();
      current_uiimage_A = null;
}   

正しく破棄されていないオブジェクトをキャッチする方法はありますか? Instruments は、ライブ バイト数が少なく、リークがほぼゼロであると報告していますが、なぜデッド オブジェクトが破棄されないのでしょうか? アプリで数分間何も起こらなかったとしても、コードで明示的に呼び出しても、GC は機能していないようです。新しい実験的なガベージ コレクターも試してみましたが、アプリを強制終了するのはさらに悪く、より迅速です。

Xamarin Web サイトで、メモリ警告のトラブルシューティングや不適切な割り当ての追跡に関するガイドラインやステップバイステップ ガイドを見つけることができません。

どんな助けや提案も大歓迎です、ありがとう!

更新: 一部の画像と uiview のサイズと解像度を減らしました。Live Bytes は 9 MB から 5 MB に減少しましたが、メモリ警告が表示された後、アプリは依然として強制終了されます。

更新 2: ハビエルが提案したように、バックグラウンド タスクを削除して直接呼び出しを行ったところ、メモリ リークはなくなりました! MonoTouch ガベージ コレクターにバグがあり、UIImages が割り当てられ、別のスレッドで破棄されたときにメモリを収集できないようです。しかし今、スクロールすると私のアプリがひどく反応しなくなったので、別のスレッドでそれを行うための解決策を見つける必要があります! しかし、どのように?

4

3 に答える 3

3

次のように、イメージ作成コードの周りで NSAutoreleasePools を使用する必要があります。

void PerformLoadImageTask(CancellationToken token)
{
    if (token.IsCancellationRequested)
    {
         Console.WriteLine("Task {0}: Cancelling", Task.CurrentId);
         return;
    }

    using (var pool = new NSAutoreleasePool ()) {
        current_uiimage_A = UIImage.FromFileUncached(file_name);
        page_A_image_view.Image = current_uiimage_A;
    }
}

オブジェクトを作成する一部の API では、オブジェクトは自動解放プールに追加され、プールが空になるまで解放されません。MonoTouch は、すべてのユーザー スレッド (スレッドプール、TPL、および通常の System.Threading スレッドを含む) の自動解放プールを自動的に作成しますが、これらのプールは、スレッドが終了するまで排出されません (スレッドプールおよび TPL スレッドの場合は制御できません)。したがって、解決策は、重要なコードの周りで NSAutoreleasePool を使用することです (プールは、破棄されると自動的にドレインされます)。

于 2012-05-03T23:02:32.670 に答える
2

に問題がありましUIImage.FromFileた。私のアプリは、タスクを使用して多くの png 画像を読み込み、メイン スレッドで表示しています。

GC.Collectバックグラウンド タスクにを追加しましたが、問題は解決していません。バックグラウンド タスクを削除し、メイン スレッドですべての作業を行い、 を呼び出す必要がありGC.Collectました。Image.Dispose が画像メモリを解放していないようです:(

しかし、別のタスクがある場合は機能しないため、削除する必要がありました:(

はい、それは動作していません....

于 2012-05-02T09:01:00.140 に答える
1

これは私にとって興味深いことです。私はメモリ管理について調べ始めたばかりです。null と object と call dispose が不可欠かどうかさえわかりません。スコープ ルールがあなたのために仕事をするはずだからです。ただし、ガベージコレクターができるだけ早くそれを行うのに役立ちます。

ガベージ コレクターの実行を明示的に要求することは要求であり、保証ではありません。

UIスレッドで作成されたオブジェクトは、UIスレッドの外側で参照されており、そこに見えると思いますが、よくわかりません

同様の問題が発生すると確信しているため、これの更新を楽しみにしています!

がんばれ、ロブ

于 2012-05-03T18:29:59.520 に答える