SqlCommand.Dispose () не содержит в нем SqlParameters - утечка памяти - C # .NET - PullRequest
5 голосов
/ 09 июня 2010

У меня есть приложение Windows Forms с MS SQL Server 2005 в качестве серверной части.Я написал код в форме для вызова нескольких хранимых процедур с использованием объектов SqlConnection, SqlCommand, и я правильно распорядился всем.

Я удалил объект sqlcommand, вызвав

oSqlCommand.Dispose()

Но я стал свидетелем моегоприложение, потребляющее огромное количество памяти.Я в основном передаю большие XML-файлы как SqlParameters.

Наконец-то я решил профилировать его с помощью профилировщика памяти RedGate, и я заметил, что System.Data.SqlClient.SqlParameters не располагаются.

Есть какие-нибудь идеи по этому поводу?

Спасибо

NLV

Ответы [ 6 ]

15 голосов
/ 09 июня 2010

Я вижу это:

Я правильно распоряжаюсь всем.

и это:

Я удалил объект 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 является поколением & ndash; он не очищает все каждый раз, когда работает. Когда объект движется к более высокому поколению, он, скорее всего, будет зависать некоторое время. Большая куча объектов - это, в основном, последнее поколение, и она совсем не очищается. Более того, он никогда не уплотняется, так что со временем фрагментируется. Это может привести к тому, что программа будет хранить гораздо больше данных, чем нужно. Что вам нужно сделать, так это попытаться найти способ не загружать все данные XML для параметра в память, чтобы он никогда не попадал в кучу больших объектов. Используйте вместо этого файловый поток или что-то подобное.

6 голосов
/ 09 июня 2010

Поскольку SqlParameter не является IDisposable, проблема не заключается в его утилизации;и обычно будет мало пользы в приведении ссылок и т. д., поскольку он все еще подлежит тому же GC.

Если звучит , как будто вы случайно сохранилиссылка на SqlCommand.Но если вы уверены , что все готово, вы можете попробовать явно установить для каждого .Value значение null и вызвать Clear() в списке параметров.Но на самом деле это просто маскировка того факта, что вы цепляетесь за мертвую команду.

3 голосов
/ 09 июня 2010

Dispose не удаляет свои параметры, а только удаляет свой внутренний кэш SqlMetaData ... Между прочим, нормально, что параметры не удаляются автоматически, потому что вы можете передать что-то, что не должно быть уничтожено после удаления команды ... + SqlParameter также не реализует Dispose, поскольку не содержит неуправляемых ресурсов ....

0 голосов
/ 18 сентября 2010

Есть несколько вещей, которые вы можете сделать, если вы уверены, что это SqlParameter, который сохраняет последнюю ссылку.

Сначала попробуйте передать ваш XML в виде строки (и использовать OPENXML в sproc для его обработки), чтобы посмотреть, поможет ли использование простого объекта и большего количества элементов управления.

Во-вторых, создайте свои собственные SqlParameter-s, сохраните их в словаре, а затем сделайте что-то вроде:

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

Затем, после того, как вы закончите выполнение команды, удалите команду, закройте (если все еще открыто) и утилизируйте соединение, перейдите в свой словарь, явно присвойте значение null как SqlParameter.Value (или возьмите строку ref из .Value в local var, присвойте String.Empty .Value и затем присвойте null локальному var - это только если SqlParameter.Value жалуется на прямое значение null. Затем присвойте null элементу словаря (который был ссылкой на SqlParameter) и затем назначьте значение null в словарь.

В более простом случае вы можете сохранить ссылку только на этот критический SqlParameter и пропустить словарь. Ключевым моментом является сохранение явного присвоения нулей - последнего ссылки на строку, а затем - последнего ссылки на SqlParameter, который ее содержал.

Помните, что здесь есть несколько вещей. Он начинается с того, что вообще не разбирает XML на среднем уровне - просто отправляет его в SQL и заканчивается явно обнуляющими ссылками. Если ваш код таков, что он на самом деле создает этот XML на лету, создайте его как большую прямую строку, чтобы попробовать.

Если это само по себе не снижает нагрузку на память, вам придется форсировать явные коллекции GC, но для этого вы должны сделать небольшое чтение и планировать разумные интервалы, так как GC стоит дорого, т.е. если вы запускаете GC-in после каждого запроса, как сумасшедший кролик, вы будете много платить в циклах процессора.

Кроме того, поскольку вы не сказали, насколько большими на самом деле являются ваши данные и на каком оборудовании вы работаете, и если ваш средний уровень работает под управлением IIS, трудно рассуждать о возможных дополнительных параметрах, например, о том, чтобы IIS запускал несколько рабочих процессов. и просто перерабатывать их, когда они слишком раздуты. Для действительно огромного потребления памяти и подлинного промежуточного уровня (т.е. без наращивания кэша) это может быть быстрее, чем возиться с gc, но мы говорим действительно огромные данные, чтобы войти в эту область.

0 голосов
/ 09 июня 2010

Я использовал этот шаблон в нескольких проектах без каких-либо проблем

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();
        }
    }
}

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

0 голосов
/ 09 июня 2010

Без проверки этого я могу думать о двух вещах, которые могут вам помочь.С SqlParameters вы можете использовать метод finalize(), который освободит ресурсы.Также вы выполняете все свои команды Sql через блок using?Если это так, когда использование блока будет завершено, ваши ресурсы должны быть восстановлены, и это устранит проблемы утечки памяти.

...