Исключение типа 'System.OutOfMemoryException' было сгенерировано. Почему?

У меня динамический запрос, который возвращает около 590 000 записей. Он запускается успешно в первый раз, но если я запустил его снова, я продолжаю получать System.OutOfMemoryException. По каким причинам это может происходить?

Ошибка происходит здесь:

  public static DataSet GetDataSet(string databaseName,string
                                   storedProcedureName,params object[] parameters)
    {
        //Creates blank dataset
        DataSet ds = null;

        try
        {
            //Creates database
            Database db = DatabaseFactory.CreateDatabase(databaseName);
            //Creates command to execute
            DbCommand dbCommand = db.GetStoredProcCommand(storedProcedureName);
            dbCommand.CommandTimeout = COMMAND_TIMEOUT;
            //Returns the list of SQL parameters associated with that stored proecdure
            db.DiscoverParameters(dbCommand);

            int i = 1;
            //Loop through the list of parameters and set the values
            foreach (object parameter in parameters)
            {
                dbCommand.Parameters[i++].Value = parameter;
            }
            //Retrieve dataset and set to ds
            ds = db.ExecuteDataSet(dbCommand);
        }
            //Check for exceptions
        catch (SqlException sqle)
        {
            throw sqle;
        }
        catch (Exception e)
        {
            throw e; // Error is thrown here.
        }
        //Returns dataset
        return ds;
    }

Вот код запускается при нажатии кнопки:

protected void btnSearchSBIDatabase_Click(object sender, EventArgs e)
{

        LicenseSearch ls = new LicenseSearch();

        DataTable dtSearchResults = new DataTable();

        dtSearchResults = ls.Search();

        Session["dtSearchResults"] = dtSearchResults;

        Response.Redirect("~/FCCSearch/SearchResults.aspx");
        }
        else
            lblResults.Visible = true;
    }
10.12.2008 16:22:06
590K строк - это немного излишне, не правда ли?
StingyJack 10.12.2008 16:40:11
Разве проблема не в том, что вы просто храните DataTable в Session, а затем при повторном запросе у вас в памяти есть переменные Session и ваш оригинальный DataTable? Кстати, не вызывайте throw e;, вызывайте, throw;иначе трассировка стека скажет, что catchобработчик сгенерировал исключение, когда фактически это содержащий код.
Dominic Zukiewicz 29.05.2012 12:46:07
5 ОТВЕТОВ
РЕШЕНИЕ

Он запускается успешно в первый раз, но если я запускаю его снова, я получаю исключение System.OutOfMemoryException. По каким причинам это может происходить?

Независимо от того, что сказали другие, ошибка не имеет ничего общего с забыванием утилизировать DBCommand или DBConnection, и вы не исправите свою ошибку, выбрав одну из них.

Ошибка связана с вашим набором данных, который содержит почти 600 000 строк данных. Очевидно, ваш набор данных потребляет более 50% доступной памяти на вашем компьютере. Очевидно, вам не хватит памяти, когда вы вернете другой набор данных того же размера до того, как первый будет собран сборщиком мусора. Просто как тот.

Вы можете решить эту проблему несколькими способами:

  • Попробуйте вернуть меньше записей. Лично я не могу себе представить, чтобы когда-нибудь возвращение 600K записей было полезным для пользователя. Чтобы свести к минимуму возвращаемые записи, попробуйте:

    • Ограничение вашего запроса до первых 1000 записей. Если по запросу возвращено более 1000 результатов, сообщите пользователю о необходимости сузить результаты поиска.

    • Если ваши пользователи действительно настаивают на том, чтобы видеть столько данных одновременно, попробуйте разбить их на страницы. Помните: Google никогда не показывает вам все 22 баджиллионных результата поиска одновременно, он показывает вам около 20 записей одновременно. Google, вероятно, не хранит в памяти все 22 баджиллионных результата одновременно, вероятно, он считает, что его память более эффективна для запроса базы данных для создания новой страницы.

  • Если вам просто нужно перебрать данные и вам не нужен произвольный доступ, попробуйте вместо этого вернуть устройство чтения данных. Считыватель данных одновременно загружает только одну запись в память.

Если ни одна из этих опций не является опцией, вам нужно заставить .NET освободить память, используемую набором данных, прежде чем вызывать ваш метод одним из следующих методов:

  • Удалите все ссылки на ваш старый набор данных. Все, что удерживает референс вашего набора данных, не позволит ему восстановить память.

  • Если вы не можете обнулить все ссылки на ваш набор данных, удалите все строки из набора данных и любые объекты, связанные с этими строками. Это удаляет ссылки на датаров и позволяет их использовать сборщику мусора.

Я не верю, что вам нужно позвонить, GC.Collect()чтобы вызвать цикл генов. Мало того, что это вообще плохая идея для вызова GC.Collect(), потому что достаточное давление памяти приведет к тому, что .NET вызовет сборщик мусора самостоятельно.

Примечание. Вызов Dispose для вашего набора данных не освобождает память, не вызывает сборщик мусора и не удаляет ссылку на ваш набор данных. Dispose используется для очистки неуправляемых ресурсов, но DataSet не имеет никаких неуправляемых ресурсов. Он реализует только IDispoable, потому что он унаследован от MarshalByValueComponent, поэтому метод Dispose для набора данных практически бесполезен.

43
10.12.2008 17:15:06
Здравствуйте, 754600 строк, если быть точным, возвращается, если пользователь не выбирает никаких дополнительных критериев и выполняет запрос. У меня действительно включена подкачка страниц, но все результаты все еще возвращаются в набор данных.
Xaisoft 10.12.2008 17:14:56
Я столкнулся с этой проблемой и считаю, что гораздо эффективнее запрашивать базу данных каждый раз, когда пользователь хочет просмотреть новую страницу. Требуется больше кода, но другой работы нет. Используйте этот код, чтобы помочь вам найти
Juliet 10.12.2008 17:17:40

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

7
10.12.2008 16:25:06
Вы можете использовать различные профили памяти ... Это лучший, который я встречал: memprofiler.com
Kieron 10.12.2008 16:29:55
В этом примере, который вы только что опубликовали, убедитесь, что команда и соединение были закрыты / удалены.
Kieron 10.12.2008 16:31:08
Спасибо, я собираюсь дать ему шанс. Я никогда не использовал профилировщик памяти раньше.
Xaisoft 10.12.2008 16:31:09
Вы имеете в виду DbCommand, верно? Какую часть образца я бы закрыл и утилизировал? Я также использую библиотеку Enterprise, автоматически ли закрывается соединение?
Xaisoft 10.12.2008 16:34:04
Каждый раз, когда код выполняется выше, DbCommand всегда равен нулю. Разве это не означает, что он автоматически закрывается?
Xaisoft 10.12.2008 16:40:37

Вы явно не избавляетесь от вещей.

Рассмотрите команду «using», когда временно используете объекты, которые реализуют IDisposable.

3
10.12.2008 16:32:36

Где это терпит неудачу?

Я согласен, что ваша проблема, вероятно, в том, что ваш набор данных из 600 000 строк, вероятно, слишком велик. Я вижу, что вы добавляете его в сеанс. Если вы используете состояние сеанса Sql, он также должен будет сериализовать эти данные.

Даже если вы правильно распоряжаетесь своими объектами, у вас всегда будет как минимум 2 копии этого набора данных в памяти, если вы запустите его дважды, один раз в сеансе, один раз в процедурном коде. Это никогда не будет масштабироваться в веб-приложении.

Посчитайте, что 600 000 строк при 1-128 битном guid на строку дадут 9,6 мегабайта (600k * 128/8) только данных, не говоря уже об издержках набора данных.

Обрежьте свои результаты.

0
10.12.2008 18:00:21

попытайтесь разбить ваши большие данные как можно больше, потому что я уже сталкивался с такими проблемами. В котором у меня более 10 записей Lakh с 15 столбцами.

1
13.04.2012 06:39:51