5

バックエンドとして MS SQL Server 2005 を使用した Windows フォーム アプリケーションを使用しています。SqlConnection、SqlCommand オブジェクトを使用していくつかのストアド プロシージャを呼び出す形式でコードを記述し、すべてを適切に破棄しました。

呼び出して sqlcommand オブジェクトを破棄しました

oSqlCommand.Dispose()

しかし、アプリケーションが大量のメモリを消費しているのを目の当たりにしました。基本的に、大きな XML ファイルを SqlParameters として渡します。

最終的に、RedGate メモリ プロファイラを使用してメモリ プロファイリングを行うことにしましたが、それらが破棄System.Data.SqlClient.SqlParametersされていないことに気付きました。

これに関する洞察はありますか?

ありがとう

NLV

4

6 に答える 6

15

私はこれを見る:

私はすべてを適切に処分します。

この:

呼び出して sqlcommand オブジェクトを破棄しましたoSqlCommand.Dispose()

ただし、それらは相互に排他的です。.Dispose()直接電話したらダメです。Dispose()具体的には、例外によってプログラムがメソッドの呼び出しをスキップする可能性を残します。コマンドを破棄する「適切な」方法では、次のように using ブロックを使用してコマンドを作成します。

using (SqlCommand cmd = new SqlCommand("sql string here"))
{
    // use the command here
} // compiler transforms your code to make sure .Dispose() is called here

さて、これは現時点での主な問題ではありませんが、家に帰る価値のあるポイントです.

パラメータに関する質問については、SqlParameters は IDisposable を実装していません。したがって、それらを直接破棄することはありません。それらは完全に管理されたリソースであり、到達できなくなった後、ある時点でガベージ コレクターによってクリーンアップされることを意味します。それらを自分でクリーンアップするために何もする必要はありません。

SqlParameter オブジェクトが本来あるべき時間よりもずっと長くぶら下がっていることを真剣に示すことができる場合は、それらへの参照をどこかに保持していることを意味します。たとえば、古い SqlCommand オブジェクトをどこかに「キャッシュ」している可能性があります。これにより、すべてのパラメーターが保持されます。そうしないでください。まだ SqlParameters を参照しているものを見つけて削除すると、ガベージ コレクターがそれらをクリーンアップします。

アップデート:

質問を読み直した後、xml パラメーターがラージ オブジェクト ヒープに配置されているように思えます。.Net のガベージ コレクターは世代別です。実行するたびにすべてをクリーンアップするわけではありません。オブジェクトがより高い世代に移動するにつれて、しばらくぶらぶらする可能性が高くなります。ラージ オブジェクト ヒープは基本的に最後の世代であり、クリーンアップされることはほとんどありません。それ以上に、時間の経過とともに断片化するように、圧縮されることはありません。これにより、プログラムが必要以上のデータを保持する可能性があります。必要なことは、パラメーターの xml データ全体がメモリに読み込まれないようにする方法を見つけて、大きなオブジェクト ヒープに到達しないようにすることです。代わりにファイル ストリームなどを使用してください。

于 2010-06-09T19:19:23.610 に答える
6

SqlParameterではないので、IDisposableそれを処分する問題ではありません。通常、参照などを整理してもほとんどメリットはありません。同じ GC の対象となるためです。

への参照を誤って保持しているように聞こえる場合は、 SqlCommand. ただし、完了したことが確実.Valueな場合は、それぞれを明示的に に設定して、パラメーター リストnullを呼び出すことができます。Clear()しかし、それはあなたが死んだコマンドにしがみついているという事実を隠しているだけです.

于 2010-06-09T19:22:40.637 に答える
3

Dispose はそのパラメーターを Dispose せず、内部 SqlMetaData キャッシュのみを破棄します...ところで、コマンドを破棄した後に破棄してはならないものを渡すことができるため、パラメーターが自動的に破棄されないのは正常です... + SqlParameter は実装されていません管理されていないリソースを保持していないため、いずれかを破棄します....

于 2010-06-09T19:16:42.303 に答える
0

これをテストしなくても、役立つ可能性のある 2 つのことを考えることができます。を使用すると、リソースを解放する方法をSqlParameters使用できます。また、ブロックfinalize()を介してすべての Sql コマンドを実行していますか? usingその場合、使用中のブロックが終了したら、リソースを再利用する必要があり、メモリ リークの問題が解消されます。

于 2010-06-09T19:20:08.953 に答える
0

最後の参照を保持しているのは SqlParameter であると確信できる場合、できることがいくつかあります。

最初に、XML を文字列として渡してみてください (そして、それを処理するために sproc で OPENXML を使用します) 単純なオブジェクトとより多くの制御が役立つかどうかを確認してください。

次に、独自の SqlParameter-s を作成し、辞書に保持してから、次のようにします。

foreach (SqlParameter param in parameters.Values)
    command.Parameters.Add(param);

次に、コマンドの実行を終了してコマンドを破棄し、(まだ開いている場合は) 接続を閉じて接続を破棄し、辞書に移動して、null を SqlParameter.Value として明示的に割り当てます (または、文字列 ref を .Value からローカル変数に取得します)。 String.Empty を .Value に割り当ててから、null をローカル var に割り当てます。これは、SqlParameter.Value が単純な null について文句を言う場合のみです。次に、null を辞書項目 (これは SqlParameter への参照でした) に割り当て、次に null を辞書に割り当てます。

より単純なケースでは、その 1 つの重要な SqlParameter への参照のみを保持し、辞書をスキップできます。重要なポイントは、null を明示的に割り当て続けることです。つまり、最後の参照を文字列に割り当て、次にそれを含む SqlParameter の最後の参照に割り当てます。

いくつかのことが関係していることを覚えておいてください。中間層で XML をまったく解析しないことから始まります。XML を SQL に送信するだけで、明示的に参照を無効にすることで終わります。コードが実際にその場で XML を構築するようなものである場合は、1 つを大きなストレート文字列として作成してみてください。

これだけでメモリ負荷が下がらない場合は、明示的な GC コレクションを強制する必要がありますが、そのためには、GC のコストがかかるため、読み取りを少し行い、適切な間隔を計画する必要があります。つまり、リクエストごとに GC-in を開始する場合です。狂ったウサギのように、CPU サイクルで多額の費用を支払うことになります。

また、実際のデータの大きさや実行しているハードウェアの種類について言及していないため、中間層が IIS で実行されている場合、IIS で複数のワーカー プロセスを実行してリサイクルするなど、考えられるさらなるオプションについて推測するのは困難です。彼らが肥大化しすぎたとき。非常に大量のメモリ消費と真のパススルー中間層 (キャッシュの蓄積がないことを意味する) の場合、gc をいじるよりも高速ですが、その領域に入るために非常に巨大なデータについて話しています。

于 2010-09-18T16:35:18.533 に答える
0

私はこのパターンを問題なくいくつかのプロジェクトで使用しました

public partial class StoredProcedures
{
    [SqlProcedure()]
    public static void InsertCurrency_CS(
        SqlString currencyCode, SqlString name)
    {
        using (SqlConnection conn = new SqlConnection("context connection=true"))
        {
            SqlCommand InsertCurrencyCommand = new SqlCommand();
            SqlParameter currencyCodeParam = new SqlParameter("@CurrencyCode", SqlDbType.NVarChar);
            SqlParameter nameParam = new SqlParameter("@Name", SqlDbType.NVarChar);



            InsertCurrencyCommand.CommandText =
                "INSERT Sales.Currency (CurrencyCode, Name, ModifiedDate)" +
                " VALUES(@CurrencyCode, @Name)";

            InsertCurrencyCommand.Connection = conn;

            conn.Open();
            InsertCurrencyCommand.ExecuteNonQuery();
            conn.Close();
        }
    }
}

参照: http://msdn.microsoft.com/en-us/library/5czye81z%28VS.80%29.aspx

于 2010-06-09T19:32:56.327 に答える