2015-06-04 4 views
8

У меня есть процесс загрузки данных, который загружает большой объем данных в DataTable, а затем обрабатывает некоторые данные, но каждый раз, когда задание заканчивается, DataLoader.exe (32 бит имеет ограничение на 1,5 ГБ памяти) не освобождает всю используемую память.DataTable не освобождает память

Я попытался 3 способа, чтобы освободить память:

  1. DataTable.Clear(), а затем называют DataTable.Dispose() (Release около 800 МБ памяти, но все-таки увеличить 200 МБ памяти каждый раз, когда загрузка данных задания отделки, после того, как 3 или 4 раза больше загрузки данных, из-за исключения из памяти, потому что оно превышает 1,5 Гб памяти)
  2. Установить значение DataTable в null (освободить память и выбрать загрузку большего количества данных, исключение из памяти)
  3. вызов DataTable.Dispose() напрямую (освобождение памяти отсутствует, и если вы выбрали загрузку большего количества данных, исключение из памяти исключено)

Ниже приведен код, который я попробовал для тестирования (в реальной программе он не называется рекурсивно, он запускается с помощью некоторой логики просмотра каталога. Этот код предназначен только для тестирования. Извините за сумбур).

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data; 

namespace DataTable_Memory_test 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      LoadData();     
      Console.ReadKey(); 

     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.ToString()); 
      Console.ReadKey(); 
     } 
    } 

    private static void LoadData() 
    { 
     DataTable table = new DataTable(); 
     table.Columns.Add("Dosage", typeof(int)); 
     table.Columns.Add("Drug", typeof(string)); 
     table.Columns.Add("Patient", typeof(string)); 
     table.Columns.Add("Date", typeof(DateTime)); 

     // Fill the data table to make it take about 1 G memory. 
     for (int i = 0; i < 1677700; i++) 
     { 
      table.Rows.Add(25, "Indocin", "David", DateTime.Now); 
      table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now); 
      table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now); 
      table.Rows.Add(21, "Combivent", "Janet", DateTime.Now); 
      table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now); 
     } 
     Console.WriteLine("Data table load finish: please check memory."); 
     Console.WriteLine("Press 0 to clear and dispose datatable, press 1 to set datatable to null, press 2 to dispose datatable directly"); 
     string key = Console.ReadLine(); 
     if (key == "0") 
     { 
      table.Clear(); 
      table.Dispose(); 
      Console.WriteLine("Datatable disposed, data table row count is {0}", table.Rows.Count); 
      GC.Collect(); 
      long lMemoryMB = GC.GetTotalMemory(true/* true = Collect garbage before measuring */)/1024/1024; // memory in megabytes 
      Console.WriteLine(lMemoryMB); 

     } 
     else if (key == "1") 
     { 
      table = null; 
      GC.Collect(); 
      long lMemoryMB = GC.GetTotalMemory(true/* true = Collect garbage before measuring */)/1024/1024; // memory in megabytes 
      Console.WriteLine(lMemoryMB); 
     } 
     else if (key == "2") 
     { 
      table.Dispose(); 
      GC.Collect(); 
      long lMemoryMB = GC.GetTotalMemory(true/* true = Collect garbage before measuring */)/1024/1024; // memory in megabytes 
      Console.WriteLine(lMemoryMB); 
     } 
     Console.WriteLine("Job finish, please check memory"); 
     Console.WriteLine("Press 0 to exit, press 1 to load more data and check if throw out of memory exception"); 
     key = Console.ReadLine(); 
     if (key == "0") 
     { 
      Environment.Exit(0); 
     } 
     else if (key == "1") 
     { 
      LoadData(); 
     } 
    } 
    } 
} 
+1

'сама DataTable' не реализует' Dispose() ', он получает метод от его родителя' MarshalByValueComponent' и только две вещи, 'MarshalByValueComponent' делает вызов' сайта .Container.Remove (this) ', если у вас есть таблица данных внутри [' ISite'] (https://msdn.microsoft.com/en-us/library/system.data.datatable.site (v = vs. 110) .aspx) (что вы не делаете в своем примере кода) и поднимите ['Disposed'] (https://msdn.microsoft.com/en-us/library/system.componentmodel.marshalbyvaluecomponent.disposed (v = vs.110) .aspx). Он не выделяет никаких ресурсов. –

+0

Я не знаю, но я уверен, вы получаете свои объекты, но у вас проблемы с фрагментацией памяти. EDIT: OH, запустите свою программу без отладчика, подключенного в режиме деблокирования, вы увидите другое поведение. ГК ведет себя очень сильно, когда подключен отладчик. –

+0

@ScottChamberlain Спасибо. Но в моем случае эта загрузка данных находится внутри каталога, смотрящего exe. Чтобы предотвратить исключение OutOfMemoery, что мне делать? – mhan0125

ответ

1

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

OracleBulkCopy Memory Leak(OutOfMemory Exception)

1

Там на самом деле не способ заставить C#, чтобы освободить память, как вы бы с кодом, который не имеет управления памятью. Это помогает понять, как работает сборщик мусора .NET. В основном использование памяти в приложениях .NET возрастает до одного из трех условий, которые запускают сборку мусора. Я описать процесс в ответе на следующий вопрос:

Cleaning up variables in methods

Один из способов избежать OutOfMemory исключения является использование MemoryFailPoint класса, который позволяет установить неисправность точки, за которой InsufficientMemoryException брошена, давая вам возможность замедлить процесс до тех пор, пока не появится другой рабочий поток. Я не уверен, если это то, что вы хотите попробовать, но она доступна для вас:

https://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint%28v=vs.100%29.aspx?f=255&MSPPError=-2147217396

+1

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

+0

Пользователь видел ошибку «OutOfMemory», когда он это делает. Это хорошая идея использовать объекты в узком пространстве, но просто установить его на «null» недостаточно для запуска сбора мусора, как я уже говорил выше. – maniak1982

+1

Не сказал, что это так. НЕ устанавливая значение null, но предотвращает сбор мусора. Во всяком случае, я только заметил, OP вызывает рекурсивно LoadData, по сути никогда не выпуская старые DataTables. –

3

Если переместить часть, когда вы просите, чтобы подтвердить вне функции, память освобождается правильно (только испытанный метод 1 (Clear и Dispose)):

static void Main(string[] args) 
{ 
    try 
    { 
     string key; 
     do 
     { 
      LoadData(); 
      Console.WriteLine("Job finish, please check memory"); 
      Console.WriteLine("Press 0 to exit, press 1 to load more data and check if throw out of memory exception"); 
      key = Console.ReadLine(); 
     } while (key == "1"); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.ToString()); 
     Console.ReadKey(); 
    } 
} 

Возможно, память Объектами освобождается, когда они находятся вне сферы

+0

Запуск в режиме деблокирования без отладчика исправил бы это, OP устал, и все еще получил ошибку OOM. –

+0

Я думаю, что путь codroipo прав, он освобождает память после каждого прогона. – mhan0125

+1

@ScottChamberlain, сохраняя ссылку на DataTable, OP предотвращает сбор GC любых оставшихся данных, например данные отслеживания изменений. Запуск в режиме Release не изменит этого.Простое решение - это то, что предлагает codroipo - не используйте повторно переменную, нуль или лучше, переместите ее в другом методе, чтобы ее можно было собрать, когда она выходит из области видимости. –

2

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

Когда в отладочной сборке или сборке релизов с присутствующим отладчиком все объекты имеют срок службы, распространяющиеся на все время жизни метода. То, что это означает, table не может быть исправлено GC, пока вы не завершили метод LoadData. Вот почему у вас не хватает памяти.

Если вы меняете свою программу в режиме деблокирования и запускаете ее без отладчика, то, как только вы передадите последнюю ссылку на объект, переменная table указывает на ваш путь к коду, объект становится пригодным для сбора мусора, и вы получаете память освобождена.

Причина, по которой GC изменяет свое поведение во время «отлаживаемой ситуации», предполагает, что сам отладчик содержит ссылку на все переменные, которые находятся в области текущего исполняемого кода. Если бы вы этого не сделали, вы бы не смогли посмотреть на значение переменной в окне просмотра или на ней. Из-за этого вы не можете «передать последнюю ссылку на объект» до тех пор, пока переменная не выйдет из области действия или вы не перезапите переменную.

Для получения более подробной информации о процессе см. Запись в блоге On Garbage Collection, Scope and Object Lifetimes.

Смежные вопросы