2

私はC#の初心者です。最近、アプリケーション(vs2008)を更新すると、次の問題が発生しました。

アプリケーションには、次のような C++ 関数ヘルパーがあります。

array<float>^ Variant::CopyToFloats()
{
   unsigned int n = this->data_uint8->Length;
   array<float>^ dst = gcnew array<float>(n); //<<OutOfMemoryException happened here
   for (unsigned int i = 0; i < n; i++)
    dst[i] = (float)this->data_uint8[i];
   return dst;
}

c# ファイルでは、

    for(int i=0; i<m; i++)
    {  for(int j=0; j<n; j++)
       {
         float[] scan = data[i].CopyToFloats();
         for(int k=0; k<nn; k++)
           sample[k]=scan[function(i,j)];
       }
   }

アプリケーションを実行すると、OutOfMemoryException が発生します。

次に、次のコードを追加しました

   Process proc = Process.GetCurrentProcess();
   long memory = proc.PrivateMemorySize64;

外側のループの前後で、スキャンのメモリが解放されていないことがわかりました。

私は次の方法を試しました:

1.GC.Collect()を使用して/使用せずに、スキャンをクリアしてnullに設定します

for(int i=0; i<m; i++)
{  for(int j=0; j<n; j++)
   {
     float[] scan = data[i].CopyToFloats();
     for(int k=0; k<nn; k++)
       sample[k]=scan[function(i,j)];
    }
    Array.Clear(scan, 0, scan.Length);
    scan = null;
    //GC.Collect();
}

GC.Collect() を呼び出すと、プログラムの実行が非常に遅くなりました。呼び出さなくても、プログラムは OOME としてクラッシュしました。

どのメモリが解放されていないのだろうか?gcnew によって作成されたスキャンまたはアレイ?

2.配列のサイズが大きい(>500000)ので、ループに入る前に大きなサイズの配列を割り当てます。

    float[] scan = new float[data[0].GetSize()];
    for(int i=0; i<m; i++)
    {  for(int j=0; j<n; j++)
       {
          scan = data[i].CopyToFloats();
          for(int k=0; k<nn; k++)
             sample[k]=scan[function(i,j)];
       }
   }

しかし、OOME はまだ発生しました。ここから、gcnew で作成した配列のメモリが解放されていないことは確かです。私は正しいですか?私が正しければ、なぜリリースされなかったのですか?この記憶を解放する方法はありますか?私が正しくない場合は、アドバイスをお願いします。

4

2 に答える 2

1

大きなオブジェクト ヒープの断片化が発生している可能性があります。これは、発生するメモリ不足エラーを説明しますが、同じサイズの配列のみを割り当てると断片化がどのように発生するかはわかりません。

必要に応じて各要素のみをキャストするという Pavel の提案は、最良の選択のように聞こえます。あなたのケースでそれが不可能または望まれない場合は、大きな配列を事前に割り当てることをお勧めしますが、投稿された例にはバグがあるようです:

float[] scan = new float[data[0].GetSize()];
for(int i=0; i<m; i++)
{  for(int j=0; j<n; j++)
   {
      temp = data[i].CopyToFloats(); // you still allocate a new array here!
      for(int k=0; k<nn; k++)
         sample[k]=scan[function(i,j)]; // scan has not been updated!
   }
}

代わりに、次のtemp = data[i].CopyToFloats();ようなことをする必要があります
data[i].CopyToFloats(scan)。事前に割り当てられた配列を使用できるようにするには、C++ 関数のシグネチャを変更する必要があります。


いくつかの追加のアイデア:

新しい配列を作成すると OutOfMemory 例外が発生するからといって、これは配列がリークしているリソースであることを意味しません。配列が毎回 GC によって正常にクリーンアップされる可能性は十分にありますが、他のオブジェクトはそうではありません。

data[] は Variant の配列ですか、それとも data は実際にはindexerを持つカスタム コレクション クラスですか?
もしそうなら、私はインデクサーが問題であると強く疑っています。

GC.WaitForPendingFinalizers()の代わりに使用すると、プログラムはクラッシュせずに実行されますGC.Collect()か?
もしそうなら、あなたの問題はファイナライザースレッドを詰まらせているファイナライザーを持つオブジェクトです。これは、新しいオブジェクトがファイナライズされるよりも早く作成された場合に発生します。デストラクタを持つすべての C++/cli クラスがその候補です。

于 2013-05-11T21:11:50.350 に答える
1

配列全体を float に変換するのではなく、必要な値をキャストする方が簡単かもしれません

for(int i=0; i<m; i++)
{  for(int j=0; j<n; j++)
   {
     byte[] scan = data[i];
     for(int k=0; k<nn; k++)
       sample[k]=(float)scan[function(i,j)];
   }
}

あなたの質問に直接答えることはありませんが、メモリを使いすぎる必要性を取り除くことができます

于 2013-05-11T19:25:29.463 に答える