У меня есть часть кода, который воздействует на большие массивы double
(содержащий приблизительно 6 000 элементов, по крайней мере), и выполняется несколько сотен раз (обычно 800).
Когда я использую стандартный цикл, как этот:
double[] singleRow = new double[6000];
int maxI = 800;
for(int i=0; i<maxI; i++)
{
singleRow = someObject.producesOutput();
//...
// do something with singleRow
// ...
}
Использование памяти повышается приблизительно для 40 МБ (от 40 МБ в beggining цикла к 80 МБ в конце).
Когда я вызываю для использования сборщика "мусора" для выполнения при каждом повторении, использование памяти остается на уровне 40 МБ (повышение является незначительным).
double[] singleRow = new double[6000];
int maxI = 800;
for(int i=0; i<maxI; i++)
{
singleRow = someObject.producesOutput();
//...
// do something with singleRow
// ...
GC.Collect()
}
Но время выполнения в 3 раза дольше! (это крайне важно),
Как я могу вынудить C# использовать ту же область памяти вместо того, чтобы выделить новые?Примечание: У меня есть доступ к коду someObject
класс, поэтому если это было бы необходимо, я могу изменить его.
Почему вы выделяете большой пустой singleRow
только для его перезаписи? Возможно, вам следует передать массив, чтобы его значения изменились на месте. Это позволит вам повторно использовать его.
double[] singleRow = new double[6000];
int maxI = 800;
for(int i=0; i<maxI; i++)
{
someObject.FillWithOutput(singleRow);
//...
// do something with singleRow
// ...
}
Если метод иногда заполняет менее 6000 элементов, он может просто вернуть счетчик заполнения. В качестве альтернативы вы можете использовать List
, что позволит изменять размер.
Вам это не понравится, но если вам придется принудительно использовать GC, вы делаешь что-то не так. Имейте в виду, что объем памяти может увеличиваться до тех пор, пока не возникнет необходимость запустить сборщик мусора - это ХОРОШО, потому что это означает, что сборщик мусора не запускается до тех пор, пока не возникнет необходимость.
Вот тест, который звучит глупо, но он может пролить свет на то, что происходит. Внутри FillWithOutput () комментируют большую часть его функциональности. Затем запустите цикл и измерьте память. Постепенно не комментируйте фрагменты, пока не увидите отметку. Теперь вы приближаетесь к тому, что вызывает «утечку».
Сделайте singleRow
параметром и передайте его вызову playsOutput
каждый раз ...
В основном ваш метод playsOutput
, вероятно, выделяет новый массив каждый раз, а повторное присвоение singleRow
просто помечает старую память как доступную для удаления, но не запускает сборщик мусора из соображений производительности.