Pavan Mand Ответов: 2

Ошибка нехватки памяти в C#


Я получаю исключение OutOfMemoryException, когда запускаю приведенный ниже блок кода.. Это запись данных из таблицы sql server в другую таблицу sql server. Количество записей-500 тысяч. Любая помощь, пожалуйста.

public void TempCopyIdentityToTest()
        {
            try
            {
                //IdentityUtility.AddToLogFile("Start Deletion previous exported details from -" + strSource);
                IMSIdenityDM IndExpDm = new IMSIdenityDM();
                //Console.WriteLine("Start clearing IMS table");
                //IndExpDm.TruncateNonIMSIdentity("IMS");
                //Console.WriteLine("End clearing IMS table");
                IndExpDm.TempCopyIdentityToTest();
                //Console.WriteLine("End Copy IMS table process");
                //IdentityUtility.AddToLogFile("Successfully completed Deletion previous exported details from -" + strSource);
                IndExpDm = null;
            }
            catch (Exception e)
            {
                IdentityUtility.AddToLogFile("IMSIdentityBM-TempCopyIdentityToTest " + e.Message);
            }
        }


Ниже приведено определение класса IMSIdenityDM.

public class IMSIdenityDM
    {
        SqlConnection cn;
        SqlCommand cmd;
        SqlDataAdapter daTransations;
        public IMSIdenityDM()
        {
            //ConfigurationEntity.FetchIMSConfiguration();
        }
        public SqlDataAdapter ExportIMSIdentityDetails()
        {
            try
            {
                cn = UtilityDM.CreateSQLConnection();
                cmd = cn.CreateCommand();
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandText = "usp_IMS_Identity_Export";
                cmd.CommandTimeout = 900;
                daTransations = new SqlDataAdapter(cmd);
                UtilityDM.CloseSQLConnection(cn);
                return daTransations;
            }
            catch (SqlException ex)
            {
                UtilityDM.CloseSQLConnection(cn);
                IdentityUtility.AddToLogFile("IMSIdenityDM - ExportIdenityTable" + ex.Message);
                return null;
            }

            catch (Exception e)
            {
                IdentityUtility.AddToLogFile("IMSIdenityDM - ExportIdenityTable" + e.Message);
                return null;
            }
        }




Добавлена трассировка стека

Stack Trace    at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more)
   at System.Data.SqlClient.SqlDataReader.Read()
   at System.Data.Common.DataAdapter.FillLoadDataRow(SchemaMapping mapping)
   at System.Data.Common.DataAdapter.FillFromReader(DataSet dataset, DataTable datatable, String srcTable, DataReaderContainer dataReader, Int32 startRecord, Int32 maxRecords, DataColumn parentChapterColumn, Object parentChapterValue)
   at System.Data.Common.DataAdapter.Fill(DataSet dataSet, String srcTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords)
   at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet)
   at ManageIdentityDM.IMSIdenityDM.CopyIMSIdentityDetails() 6/22/2020 4:12:25 PM


Что я уже пробовал:

У меня нет знаний по C#. Этот код был написан моим коллегой, который давно ушел.

F-ES Sitecore

Если вы используете SqlDataAdapter для "заполнения" набора данных, который затем обрабатываете строка за строкой, попробуйте вместо этого использовать SqlDataReader, чтобы читать данные SQL строка за строкой, а не помещать их все сразу в память.

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/retrieving-data-using-a-datareader

Pavan Mand

Я добавил трассировку стека.Похоже, что он использует SQL data reader

2 Ответов

Рейтинг:
1

Richard MacCutchan

Удаляя коомментированные строки out и переформатируя их, мы получаем:

public void TempCopyIdentityToTest()
{
    try
    {
        IMSIdenityDM IndExpDm = new IMSIdenityDM();
        IndExpDm.TempCopyIdentityToTest(); // Does this cause a closed loop?
        IndExpDm = null;
    }
    catch (Exception e)
    {
        IdentityUtility.AddToLogFile("IMSIdentityBM-TempCopyIdentityToTest " + e.Message);
    }
}

Вызов на линии 6 может быть проблемой, так как он, по-видимому, делает обратный вызов тому же методу. Однако в этой проблеме задействовано больше кода, который мы не видим.

Если в вашей компании нет программистов на C#, то я предлагаю вам нанять подрядчика или стороннюю компанию, чтобы они пришли и помогли вам.


Pavan Mand

Спасибо за ответ. Я добавил больше кода для ясности

Рейтинг:
1

OriginalGriff

В принципе, у вас заканчивается пространство стека.
Поскольку Ричард был так любезен, что выдернул мусор, я просто скопирую его и вырву остальную часть кода, не относящегося к проблеме:

public void TempCopyIdentityToTest()
{
    IMSIdenityDM IndExpDm = new IMSIdenityDM();
    IndExpDm.TempCopyIdentityToTest();
}
Когда вы впервые вызываете этот метод, он занимает небольшое пространство в стеке потоков - так что он может запомнить, куда возвращаться, и удерживать локальную переменную IndExpDm
Первое, что вы делаете, это создаете новый экземпляр и вызываете тот же метод на нем.

Новый вызов также занимает небольшое пространство в стеке потоков - так что он может запомнить, куда возвращаться, и удерживать свою локальную переменную IndExpDm - помните, что это локальные переменные, поэтому они специфичны для этого вызова метода и будут уничтожены только тогда, когда вы вернетесь из него.
Этот вызов также создает новый экземпляр и вызывает себя снова.

Каждый раз, когда вы вызываете его, метод использует больше стека - и в отличие от кучи памяти, стековая память очень мала - около 1 МБ для 32-битных и 4 МБ для 64-битных приложений.
И очень, очень быстро он заканчивается, если у вас нет предела рекурсии - и тогда вы получаете ошибку "из памяти".


F-ES Sitecore

Разве это не даст вам ошибку переполнения стека, хотя и не из-за нехватки памяти?

OriginalGriff

Обычно да - но поскольку его конструктор пуст, а класс содержит только три ссылки, то более вероятно, что пространство стека исчерпано и он сообщает об ошибке неправильно, чем действительно исчерпал кучу. Там нет ничего, что потребляло бы скудные ресурсы и очевидную проблему рекурсии ... :смеяться:

F-ES Sitecore

Я просто попробовал это сделать и действительно получил переполнение стека.