Исключение памяти массива C++/C#

Я новичок в С#. Недавно, когда я обновляю приложение (vs2008), я столкнулся со следующей проблемой.

Приложение имеет вспомогательную функцию С++ следующим образом:

array<float>^ Variant::CopyToFloats()
{
   unsigned int n = this->data_uint8->Length;
   array<float>^ dst = gcnew array<float>(n); //<<OutOfMemoryException happened here
   for (unsigned int i = 0; i < n; i++)
    dst[i] = (float)this->data_uint8[i];
   return dst;
}

В файле С#,

    for(int i=0; i<m; i++)
    {  for(int j=0; j<n; j++)
       {
         float[] scan = data[i].CopyToFloats();
         for(int k=0; k<nn; k++)
           sample[k]=scan[function(i,j)];
       }
   }

Когда я запускаю приложение, возникает OutOfMemoryException.

Затем я добавил следующий код

   Process proc = Process.GetCurrentProcess();
   long memory = proc.PrivateMemorySize64;

до и после внешнего цикла я обнаружил, что память сканирования не освобождается.

Я пробовал следующие способы:

1. Очистите сканирование и установите для него значение null с использованием или без использования GC.Collect().

for(int i=0; i<m; i++)
{  for(int j=0; j<n; j++)
   {
     float[] scan = data[i].CopyToFloats();
     for(int k=0; k<nn; k++)
       sample[k]=scan[function(i,j)];
    }
    Array.Clear(scan, 0, scan.Length);
    scan = null;
    //GC.Collect();
}

При вызове GC.Collect() программа работала очень медленно. Без звонка программа все равно вылетала как OOME.

Мне было интересно, какая память не освобождается? сканирование или массив, созданный gcnew?

2. Поскольку размер массива большой (> 500000), я выделяю массив большого размера перед входом в цикл.

    float[] scan = new float[data[0].GetSize()];
    for(int i=0; i<m; i++)
    {  for(int j=0; j<n; j++)
       {
          scan = data[i].CopyToFloats();
          for(int k=0; k<nn; k++)
             sample[k]=scan[function(i,j)];
       }
   }

Но OOME все же случилось. Отсюда я почти уверен, что память массива, созданного gcnew, не была освобождена. Я прав? Если я прав, то почему его не выпустили? Есть ли способ освободить эту память? Если я не прав, дайте мне совет, спасибо!


person yongqiang    schedule 11.05.2013    source источник


Ответы (2)


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

for(int i=0; i<m; i++)
{  for(int j=0; j<n; j++)
   {
     byte[] scan = data[i];
     for(int k=0; k<nn; k++)
       sample[k]=(float)scan[function(i,j)];
   }
}

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

person Pavel Krymets    schedule 11.05.2013

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

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

float[] scan = new float[data[0].GetSize()];
for(int i=0; i<m; i++)
{  for(int j=0; j<n; j++)
   {
      temp = data[i].CopyToFloats(); // you still allocate a new array here!
      for(int k=0; k<nn; k++)
         sample[k]=scan[function(i,j)]; // scan has not been updated!
   }
}

Вместо temp = data[i].CopyToFloats(); нужно сделать что-то вроде
data[i].CopyToFloats(scan). Вам нужно изменить подпись вашей функции С++, чтобы иметь возможность использовать предварительно выделенный массив.


некоторые дополнительные идеи:

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

Является ли data[] массивом Variant, или данные фактически являются пользовательским классом коллекции с индексатор?
Если это так, я очень подозреваю, что проблема в индексаторе.

Работает ли программа без сбоев, если вы используете GC.WaitForPendingFinalizers() вместо GC.Collect()?
Если да, то ваша проблема заключается в том, что какой-то объект с финализатором засоряет поток финализатора. Это произойдет, если новые объекты создаются быстрее, чем они могут быть доработаны. Каждый класс C++/cli с деструктором является кандидатом на это.

person HugoRune    schedule 11.05.2013
comment
Ошибка, которую вы упомянули, это моя опечатка. Я исправил это в своем посте. Спасибо!! - person yongqiang; 12.05.2013
comment
Программа по-прежнему работает со сбоем при использовании GC.WaitForPendingFinalizers(). - person yongqiang; 12.05.2013
comment
То, как вы изменили опечатку, по-прежнему не использует один и тот же массив повторно. CopyToFloats будет каждый раз создавать новый массив. Не имеет значения, содержал ли scan другой массив до этого, он не будет повторно использован или перезаписан, вы только изменяете массив, на который указывает ссылка scan. Вам нужно передать массив, который вы хотите повторно использовать, в качестве параметра CopyToFloats, и CopyToFloats должен использовать этот массив вместо создания другого с помощью gcnew. Повторное использование массива невозможно без изменения функции C++ CopyToFloats. - person HugoRune; 12.05.2013
comment
Очень ценю ваш ответ. Я изменил функцию C++ на void Variant::CopyToFloats(array‹float›^ dst) {unsigned int n = this-›data_uint8-›Length; for (unsigned int i = 0; i ‹ n; i++) dst[i] = (float)this-›data_uint8[i]; } и измените код C# следующим образом: float[] scan = new float[data[0].GetSize()]; for(int i=0; i‹m; i++) { for(int j=0; j‹n; j++) { data[i].CopyToFloats(scan); for(int k=0; k‹nn; k++) sample[k]=scan[function(i,j)];} } Затем, когда я запускаю программу, она работает очень-очень медленно. Правилен ли мой путь? В гугле полезных советов не нашел. - person yongqiang; 12.05.2013
comment
этот код кажется мне правильным. Он точно не должен работать медленнее оригинальной версии; если это происходит, происходит что-то очень странное. - person HugoRune; 14.05.2013