.NETでゴミコレクターの自由メモリをより簡単にする方法はありますか?
-
01-10-2019 - |
質問
.NETで自由なメモリをスピードアップする方法があるかどうかを考えてきました。重要なグラフィックスが必要ない.NET(マネージドコードのみ)でゲームを作成していますが、パフォーマンスを無に失わないように適切に書きたいと思います。
たとえば、もう必要ないオブジェクトにnull値を割り当てると便利ですか?インターネット上のいくつかのサンプルでこれを見ています。
解決
もう必要ないオブジェクトにnull値を割り当てると便利ですか?
一般的に、いいえ。これを行うオンラインで表示されるサンプルのほとんどは、VB6の.NETに来た人々によるものであり、これは一般的なベストプラクティスでした。 .netでは、あまり役に立ちません。非常に長い実行方法がある場合、ゴミコレクターが少し早くオブジェクトを見つけることができますが、あなたの方法が長い場合、他の問題があります。
代わりに、.NETでは、短いメソッドを作成し、可能な限り小さなスコープブロックで変数をできるだけ遅く定義する必要があります。スコープで決定された変数の「自然な」寿命を使用しますが、その自然な寿命を短くしてください。
あなたがすべき いいえ ここで少なくとも1つの他の答えが示唆しているように、あなた自身のゴミコレクションをしてください。これは実際に物事を作ることができます もっとゆっくり. 。 .NETは、世代ごとのゴミコレクターを使用します。ゴミコレクションを強制することによって、あなた そうかもしれない ターゲットオブジェクトを収集します(そうでない場合もあります - ごみ収集には保証はありません)。しかし、あなたはおそらく、高次世代にまだ収集することができない他のオブジェクトを強制し、将来収集するのが難しくなります。だからこれをしないでください。
他のヒント
心配しすぎたり、時期尚早に最適化したりしないでください。 .NETのメモリ管理は自動で非常に効率的です。 「最適化」を開始する場合は、自分が何をしているかを正確に知る必要があります。そうしないと、遅くなる可能性があります。
したがって、最初にゲームを終了し、それが遅すぎる場合はプロファイラーを使用して問題を見つけるために使用します。ほとんどの場合、それはメモリの問題ではありません。
.NET(画像とその子孫、グラフィックス、ペン、ブラシなど)のグラフィック関連オブジェクトのほとんどは、IDISPOSABLEを実装しています。特定の操作にオブジェクトを使用する場合は、次のパターンを使用しないでください。
using(var g = Graphics.FromBitmap(bmp))
{
//Do some stuff with the graphics object
}
このようにそれを行うことで、管理されていないリソースが範囲外に落ちると解放されます。
.NETにオブジェクトを割り当てて、パフォーマンスに最も影響を与えるものの1つを見つけます。これを回避するには、使用したオブジェクトをリサイクルする工場パターンを使用します。ここに簡単な一般的な実装があります:
internal class ListFactory<T>
where T: IRecyclable, new()
{
private List<T> _internalList;
private int _pointer;
public ListFactory()
{
_internalList = new List<T>();
_pointer = 0;
}
public void Clear()
{
_pointer = 0;
}
public int Count
{
get
{
return _pointer;
}
}
public List<T> InnerList
{
get
{
return _internalList;
}
}
//Either return T form the list or add a new one
//Clear T when it is being reused
public T Create()
{
T t;
//If the pointer is less than the object count then return a recycled object
//Else return a new object
if (_pointer < _internalList.Count )
{
t = _internalList[_pointer];
t.Recycle();
}
else
{
t = new T();
_internalList.Add(t);
}
_pointer ++;
return t;
}
}
私のラインルーティングアルゴリズムの場合、私は常に多くの値を維持する必要があります Routenode 次のインターフェイスを実装します。
public interface IRecyclable
{
void Recycle();
}
これらは絶えず作成され、破壊されます。これらのオブジェクトをリサイクルするには、新しい工場を作成します。
nodeFactory = new ListFactory<RouteNode>();
オブジェクトが必要な場合は、作成方法を呼び出します。
RouteNode start = nodeFactory.Create();
RouteNode goal = nodeFactory.Create();
リスト内のオブジェクトが終了したら、リストをクリアします。ポインターはリストの開始にリセットされますが、オブジェクト自体は破壊されません。実際、リサイクル方法は、オブジェクトが再び再生されたときにのみ呼び出されます。 (他のオブジェクト参照がある場合は、これを早めに行うことができます。以下のコメントを参照してください)
これは非常に素朴な実装ですが、開始する場所です。
場合によっては、不要なnullにオブジェクトを設定することが役立ちます。多くの場合、それは役に立たないまたは逆効果ですが、常にではありません。それが役立つ可能性のある4つの主要な状況:
これを行うには(そして、私は頻繁に50MBの割り当てでこれをしなければなりませんでした)。
myObj = null;
GC.Collect();
GC.WaitForPendingFinalizers();
アプリのメモリフットプリントが大幅に減少することに気付きました。理論的には、これを行う必要はありません。ただし、実際には、32ビットWindows OSを使用すると、いつでも300MBを超える2つの隣接するブロックを無料で取得でき、そのスペースが多くの小さな割り当てまたは一連の大きなものによって取り上げられる可能性があります。不必要に失敗します。ゴミコレクターは、できる限りバックグラウンドで実行されますが、今すぐ大規模な割り当てを行わなければならない場合、その一連のラインはそれを可能にするのに役立ちます。
編集:私がコメントに入れたものから、ダウンボーターのために。
全体を読んだら リコ・マリアーニによるゴミコレクションについての投稿, 、大規模で、まれな、予測不可能なメモリの割り当てがシナリオ#2に分類されることに注意してください。ホイットへ:
ルール#2
いくつかの非繰り返しのイベントが発生した場合、このイベントが多くの古いオブジェクトを死亡させた可能性が非常に高い場合は、gc.collect()を呼び出すことを検討してください。
この典型的な例は、クライアントアプリケーションを書いており、それに関連付けられている多くのデータがある非常に大きく複雑なフォームを表示する場合です。ユーザーは、XMLドキュメントや大きなデータセットなどの大きなオブジェクトを作成する可能性があるこのフォームと対話しました。フォームが閉じると、これらのオブジェクトが死んでいるため、gc.collect()はそれらに関連付けられたメモリを取り戻します。
なぜ私はこれをコレクターに電話する可能性のある時間として提案するのですか?つまり、私のいつものアドバイスは、「コレクターはセルフチューニングなので、それを台無しにしないでください」のようなものになります。なぜあなたが尋ねるかもしれない態度の変化があるのですか?
さて、ここには、過去に基づいて未来を予測しようとするコレクターの傾向が失敗する可能性が高い状況です。
ゲームで大規模な割り当てを行う場合、メモリの処理方法に注意する必要があります。ガベージコレクターは、過去のイベントに基づいて予測に取り組んでおり、32ビットマシンのメモリの大きなブロックは、適切に管理されていないと将来の割り当てに壊滅的になります。あなたがそれをしていないなら、私が間違っていると自動的に仮定しないでください。もしあなたがそれをやったなら、私はそれを適切に行う方法の説明を歓迎します(つまり、メモリをデフラグする方法は、特定の時間に50-100MBのメモリを常に割り当てることができることを確認します)。