Как я могу явно очистить байт [] - PullRequest
7 голосов
/ 05 мая 2011

Я создаю новые байтовые массивы, которые не собираются GC и живут в памяти и увеличивают частные байты. Код ниже выполняется каждые 10 секунд. Как я могу явно очистить переменную после того, как я закончу с ней?

byte[] outputMessage = new byte[10000];
//Do some work here

Ответы [ 6 ]

9 голосов
/ 05 мая 2011

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

Явным образом вы можете очистить ссылку с помощью

outputMessage = null;

ивы можете намекнуть GC, что он должен выполнить свою работу с этим:

GC.Collect();

Однако нет никакой гарантии, что ваш объект будет собран, и ручное связывание с GC обычно не требуется.

Если вы все время добавляете новый обработчик событий, удаляете ли вы старые?Если нет, то они все еще сохраняют ссылку для вас.

РЕДАКТИРОВАТЬ: для ясности и для новой информации от OP

2 голосов
/ 23 октября 2013

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

Вот краткий пример, который иллюстрирует:

void Main()
{
    const int LOOPS = 5;
    {
        Console.WriteLine("When the arrays are kept inside the method's scope:");
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (starting memory)", GC.GetTotalMemory(false)));
        for(int i = 0; i < LOOPS; i++)
            this.AllocateArray(i);
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (exited local scope)", GC.GetTotalMemory(false)));
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (after GC collection ran)", GC.GetTotalMemory(true)));

        Console.WriteLine("\nWhen the arrays are outside the method's scope:");
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (starting memory)", GC.GetTotalMemory(false)));
        var arrays = new byte[LOOPS][];
        for(int i = 0; i < LOOPS; i++)
            this.AllocateArray(i, arrays);
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (exited local scope)", GC.GetTotalMemory(false)));
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (after GC collection ran)", GC.GetTotalMemory(true)));
        arrays[0][0] = 1; // Prevent the arrays from being optimized away
    }
    Console.WriteLine("\nAll scopes exited:");
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (before GC runs)", GC.GetTotalMemory(false)));
        Console.WriteLine(String.Format("     GC Memory: {0:N0} bytes (after GC collection ran)", GC.GetTotalMemory(true)));
}

public void AllocateArray(int run)
{
    var array = new byte[20000000];
    Thread.Sleep(100); // Simulate work..
    Console.WriteLine(String.Format("[{0}] GC Memory: {1:N0} bytes (local array allocated)", run+1, GC.GetTotalMemory(false)));
    array[0] = 1; // Prevent the array from being optimized away
}

public void AllocateArray(int run, byte[][] arrays)
{
    arrays[run] = new byte[20000000];
    Thread.Sleep(100); // Simulate work..
    Console.WriteLine(String.Format("[{0}] GC Memory: {1:N0} bytes (array allocated)", run+1, GC.GetTotalMemory(false)));
}

Вывод этого выглядит примерно так (точные результаты будут отличаться):

Когда массивы находятся внутри области действия метода:
Память GC: 24 576 232 байта (начальная память)
[1] Память GC: 45 002 324 байта (выделен локальный массив)
[2] Память GC: 44 845 548 байт (выделен локальный массив)
[3] Память GC: 64 574 296 байт (выделен локальный массив)
[4] Память GC: 64 959 472 байта (выделен локальный массив)
[5] Память GC: 44 675 340 байт (выделен локальный массив)
Память GC: 44 675 340 байт (вне локальной области)
Память GC: 24 347 296 байт (после запуска сбора GC)

Когда массивы находятся за пределами области действия метода:
Память GC: 24 355 488 байт (начальная память)
[1] Память GC: 44 467 612 байт (выделен массив)
[2] Память GC: 64 681 980 байт (выделен массив)
[3] Память GC: 85 493 004 байта (выделенный массив)
[4] Память GC: 104,442,028 байт (выделен массив)
[5] Память GC: 124 450 236 байт (выделен массив)
Память GC: 124 450 236 байт (вне локальной области)
Память GC: 124 357 588 байт (после запуска сбора GC)

Все области вышли:
Память GC: 124 365 780 байт (до запуска GC)
Память GC: 24 356 996 байт (после запуска сбора GC)

Чтобы решить вашу проблему:

  1. Убедитесь, что вы не цепляетесь за ссылки на byte[] s. Байты не будут очищены, пока все ссылки на массив не будут ушел, и это полностью выходит за рамки.
  2. Явно вызовите GC.Collect() после того, как вы покинете область действия байтового массива. однажды byte[] очистится сам по себе, но вы можете скажи ему бежать вместо ожидания.
2 голосов
/ 06 мая 2011

Подумайте над этим: возможно ли инкапсулировать ваш байт [] в класс, который реализует IDisposable? После использования вы можете избавиться от объекта, явно вызвав Dispose (). Я не уверен, поможет ли это вам найти решение, но это будет моя следующая попытка.

2 голосов
/ 05 мая 2011

Трудно сказать наверняка, не видя контекст, в котором он используется. Если вы не держите ссылки на каждое outputMessage, они в конечном итоге будут собирать мусор всякий раз, когда GC решит запустить.

На что вы смотрели, чтобы увидеть, что частные байты продолжают расти и никогда не уменьшатся? Кроме того, вы работали только с подключенным отладчиком или работали в выпуске?

Вам действительно нужно создавать новый массив каждые 10 секунд? Это может быть быстрее просто Array.Clear и использовать его снова.

2 голосов
/ 05 мая 2011

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

Оставляете ли вы фокус вашего outputMessage?
- Если оно объявлено внутри метода: оставляете ли вы его или делаетеу вас есть некоторый (намеренный) бесконечный цикл, и вы в нем остаетесь?
- Если он объявлен глобальным внутри объекта класса: остается ли ваш полный класс в памяти по ссылке на это?

0 голосов
/ 25 июля 2018

Этот тестовый пример работает только в режиме выпуска. Я создаю массив с использованием класса guid (это легко). Было бы 16 элементов.

[TestMethod]
public void ByteArrayReleasesMemoryWhenTheyGoOutOfScope()
{
    // *** WORKS ONLY IN RELEASE MODE ***
    // arrange
    var weakRef = new WeakReference(null);

    // easy way to generate byte array
    var byteArray = Guid.NewGuid().ToByteArray();
    weakRef.Target = byteArray;

    // act
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    // assert
    Assert.IsFalse(weakRef.IsAlive);
}
...