2

C# で記述された XNA 4.0 アプリケーションでメモリ リークが見つかりました。プログラムは長時間 (数日) 実行する必要がありますが、数時間かけてメモリが不足し、クラッシュします。タスク マネージャーを開いてメモリ フットプリントを確認すると、プログラムがなくなるまで毎秒 20 ~ 30 KB のメモリがプログラムに割り当てられます。プロパティを設定すると、最終的に例外BasicEffect.Textureをスローするステートメントであるため、メモリリークが発生すると思います。OutOfMemory

このプログラムには約 300 個の大きな (512px) テクスチャが Texture2D オブジェクトとしてメモリに格納されています。テクスチャは正方形ではなく、2 のべき乗でもありません。これらのオブジェクトは初期化時にのみ作成されるため、Texture2D オブジェクトを動的に作成/破棄することによって引き起こされたものではないと確信しています。一部のインターフェイス要素は独自のテクスチャを作成しますが、コンストラクター内でのみ作成され、これらのインターフェイス要素がプログラムから削除されることはありません。

テクスチャ マップされた三角形をレンダリングしています。各オブジェクトを三角形でレンダリングする前に、BasicEffect.Textureプロパティを作成済みのTexture2Dオブジェクトに設定し、BasicEffect.TextureEnabledプロパティを に設定しますtrueBasicEffectこれらの各呼び出しの間に を適用します-必要な 2 倍のBasicEffect.CurrentTechnique.Passes[0].Apply() 呼び出しを行っていることは承知していますが、コードはのプロパティが変更されるたびに呼び出すヘルパー クラス内にラップされています。Apply()Apply()BasicEffect

BasicEffectアプリケーション全体で1 つのクラスを使用しており、そのプロパティを変更しApply()て、オブジェクトをレンダリングするたびに呼び出すようにしています。

まず、プロパティを変更して何度もBasicEffect.Texture呼び出すと、メモリ リークが発生する可能性があります。Apply()第二に、これは異なるテクスチャで三角形をレンダリングする適切な方法ですか? たとえば、シングルBasicEffectを使用してそのプロパティを更新しますか?

このコードはヘルパー クラスから取得したものなので、すべての綿毛を取り除き、関連する XNA 呼び出しのみを含めました。

//single BasicEffect object for entire application
BasicEffect effect = new BasicEffect(graphicsDevice);

// loaded from file at initialization (before any Draw() is called)
Texture2D texture1 = new Texture2D("image1.jpg");
Texture2D texture2 = new Texture2D("image2.jpg");

// render object 1
if(effect.Texture != texture1) // effect.Texture eventually throws OutOfMemory exception
    effect.Texture = texture1;
effect.CurrentTechnique.Passes[0].Apply();
effect.TextureEnabled = true;
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices1, 0, numVertices1, indices1, 0, numTriangles1);

// render object 2
if(effect.Texture != texture2)
    effect.Texture = texture2;
effect.CurrentTechnique.Passes[0].Apply();
effect.TextureEnabled = true;
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices2, 0, numVertices2, indices2, 0, numTriangles2);

これは XNA アプリケーションなので、1 秒あたり 60 回、Draw メソッドを呼び出します。これにより、さまざまなインターフェイス要素がすべてレンダリングされます。これは、フレームごとに 100 ~ 200 のテクスチャを描画できることを意味し、描画するテクスチャが増えるほどnew、更新/描画ループのどこにも呼び出しを行っていないにもかかわらず、メモリ不足が速くなります。私は DirectX よりも OpenGL の経験が豊富なので、明らかに、私が気付いていない管理されていないメモリを作成している舞台裏で何かが起こっています。

4

1 に答える 1

0

私が思いつく唯一の提案は、テクスチャを 1 つずつではなく、アトラスにグループ化することです。フレームごとに大量のテクスチャをレンダリングする場合、レンダリング時間が短縮され、GPU の負荷が軽減されます。これは、GPU がテクスチャを頻繁にスワップしてレンダリングする必要がないためです。これはコストのかかる操作です。私は OpenGL の知識を使用していますが、XNA は DirectX に基づいており、同様の方法でテクスチャをロードすると想定しています (OpenGL を使用できるモノゲームを使用している場合を除く)。

そうは言っても、あなたはあまり多くの情報を提供していませんでした。メモリ リークはテクスチャの切り替えが原因である可能性がありますが、別の場所から発生している可能性もあります。メモリのほとんどがテクスチャで占有されているため、他の場所ではなくテクスチャでクラッシュが発生している可能性があります。ここでいくつかのことが起こっていると思います:

  • ガベージ コレクターは、レンダリング関数内で割り当てられているすべての RAM を取得するのに十分な速度で動作していません。
  • コードの別の場所にメモリ リークがあり、代わりにここに表示されています

繰り返しますが、私があなたのコードについてほとんど知らない限り、ここに何があるかを理解するのは困難です. しかし、できる限り、いくつかの提案があります。

  • コードを実行して、物事をどのように参照しているかを見てください。クラスと構造体の内部に一時的な参照がないことを確認してください。何かを使用し、それを異なるクラスに渡し、後でそれを「破棄」と見なす場合、誰かがまだそのオブジェクトを保持していて、削除できない可能性があります。
  • ソリューションにあるすべての「新しい」キーワードを検索してください。「new」キーワードを常に使用するものがある場合、ヒープ内に大量のオブジェクトが作成されるため、これは巨大なメモリ リークになる可能性があります。ガベージ コレクターはそれらを収集する必要がありますが、ガベージ コレクターをあまり信頼しているとは言えません。最悪の場合、このメモリ リークに対処するのに十分な頻度でガベージ コレクターが機能しないことがあります。
  • テクスチャのサイズを縮小する方法を見つけます。Atlasing は、各テクスチャを独自の Texture2D にパッケージ化するオーバーヘッドを削減するソリューションです。同じファイルからテクスチャを交換するシステムで作業する必要があるため、これはもう少し作業が必要になる可能性がありますが、あなたの場合はおそらくそれだけの価値があります。
  • XNA 内に問題があると確信している場合は、Monogame と呼ばれる別の実装を試してください。XNA とまったく同じ構造に従いますが、コミュニティによって維持されます。その結果、使用しているライブラリの根幹が書き直され、ヒープを破壊しているものが修正された可能性が高くなります。

あなたへの私のアドバイスは?もしあなたが本当に OpenGL に精通していて、やっていることはかなり単純なら、私は OpenTK をチェックするでしょう。これは、OpenGL を取り込んで C# に「移植」するシン リンカー レイヤーです。すべてのコマンドはまったく同じであり、.NET ライブラリ全体を使用してすべての追加の綿毛を柔軟に使用できます。

これが役立つことを願っています!

于 2013-09-04T20:53:33.253 に答える