2014-10-08 1 views
2

Что может привести к сбою коллекции мусора C# так ужасно в моем приложении C#, где много вызовов C++, когда хорошо установленный GC.Collect разрешает проблему? Мое приложение C# делает миллионы вызовов C++, используя System.Runtime.InteropServices DllImport и CallingConvention.Cdecl, и имеет некоторые деструкторы C# для выпуска некоторой неуправляемой памяти C++. Я использую .NET Framework 4.Плохое автоматическое исполнение сборщика мусора C# со многими вызовами для взаимодействия с C++

  1. Что может вызвать приведенный ниже код в моем приложении, чтобы заставить подкачки произойти замедление выполнения ползти (потребляющий 29GB оперативной памяти на моей системе 32 ГБ и с более 4 минут до того, как я убью этот процесс), просто изменив значение ManualGC на true при использовании памяти с памятью около 600 МБ, и выполнение завершено в течение 29 секунд?

  2. Почему оставляя ManualGCfalse и изменение Write к true использование памяти крышки около 12 ГБ и позволяет завершить выполнение примерно через 59 секунд без подкачки?

Отрывок некоторого кода в моем приложении (очевидно, с некоторыми названиями изменены):

private static int callCount = 0; 
private const bool ManualGC = false; 
private const bool Write = false; 

internal static void CommonlyCalled() 
{ 
    ++callCount; 

    if (callCount % 100000 == 0) 
    { 
     if (ManualGC) 
     { 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
     } 
     if (Write) Console.WriteLine(HandleErrorsCallCount); 
    } 

    DoLogic(); 
} 

памяти измеряется с помощью "Память (Private Working Set)" колонку в диспетчере задач Windows. Поведение постоянно повторяемо.

+0

Какая версия .Net? – Blorgbeard

+0

Я использую .NET Framework 4 – JDiMatteo

+1

Трудно узнать, не видя кода, который вызывает библиотеку C++. Можете ли вы создать классы оболочки C#, которые реализуют 'IDisposable' и очищают неуправляемый материал в' Dispose() '? –

ответ

7

и имеет некоторые C# деструкторы освободить некоторых из C++ неуправляемой памяти

Это не достаточно, если Ваша обертка мала, и ваш код C++ требует много памяти. Вы просто не оказываете достаточного давления на GC, чтобы заставить его быстро называть ваши финализаторы. Реализация IDisposable для исправления этого является шаблоном. Но не полное решение, вы должны сообщить об этом GC, чтобы он мог что-то сделать.Некоторый код для игры с:

using System; 
using System.Runtime.InteropServices; 

class Program { 
    static void Main(string[] args) { 
     while (!Console.KeyAvailable) { 
      new Wrapper(); 
     } 
    } 
} 

class Wrapper { 
    private const int alloc = 10 * 1024; // C++ object memory usage 
    private readonly bool useamp = false; // Change this after testing 
    private IntPtr mem; 

    public Wrapper() { 
     if (useamp) GC.AddMemoryPressure(alloc); 
     mem = Marshal.AllocHGlobal(alloc); 
    } 
    ~Wrapper() { 
     Marshal.FreeHGlobal(mem); 
     if (useamp) GC.RemoveMemoryPressure(alloc); 
    } 
} 

Обратите внимание на использование этой программой памяти во время ее запуска. На моей машине частные байты составляют примерно половину концерта. Теперь измените useamp и запустите снова, вы увидите, что это много более эффективно, требуя всего 4 МБ. Без вызова Dispose :) Протестировано на Win 8.1, .NET 4.5.1, вы можете получить очень разные результаты на более старой версии .NET.

Значимость, которую вы выбираете для alloc не так уж и важна, она должна быть только в шале. По-видимому, вам нужно гораздо больше 10 КБ.

1

Похоже, вы полагаетесь на пользовательский финализатор для правильного поведения в своем приложении C#. Вам необходимо обновить C# код для решения следующих конкретных проблем с этим сценарием:

  1. Вы не должны использовать определенный пользователь финализаторы (С # метод с синтаксисом деструкторов C++). Если у вас есть неуправляемый ресурс, который нужно очистить, создайте класс, который расширяет SafeHandle, чтобы обернуть только дескриптор ресурса.
  2. Ваш код должен быть написан для того, чтобы явным образом вызывать Dispose() на экземплярах SafeHandle, которые завершают ваши неуправляемые ресурсы, когда вы закончите использовать их. Никогда не полагайтесь на финализатор на C# для выполнения операций очистки.

Это утверждение достаточно верно в то время, что все нижеследующее является, а правило жить:

Если вы пишете определенный пользователем финализации в C#, вы, вероятно, сделали что-то неправильно.

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