1

Тема: Великі одновимірні масиви і OutOfMemoryException

В мене виникла проблема при створенні великого одновимірного масиву-буферу. Його розмір мав бути кілька гігабайт. Як пише книжка, в .Net память під одновимірні масиви виділяється послідовно, одним шматком. Такого великої ділянки вільної памяті система не знайшла і я отримав OutOfMemoryException під час виконання.
Виникла ідея шукати найбільшу доступну ділянку памяті і формувати масив відповідного розміру. Точна кількість елементів в масиві значення не має, її можна зменшувати, якщо масив не влазить в память. Методів для визначення розмірів вільних ділянок памяті я не знайшов, тому вирішив сам щось придумати.
Створив клас з методом, куди передається тип масиву і бажаний розмір (_requiredSizeOfArray). Клас пробує створити масив заданого розміру, якщо не виходить, то OutOfMemoryException подавляється і бажаний розмір зменшується на заданий відсоток (_percentOfDecrease). Спроба повторюється знову, доки масив не буде створено.
Якщо вільної памяті взагалі немає, то повернеться масив нульвого розміру  :)
Чи знає хто кращий варіант для цієї проблеми ? якщо ні то може моя ідея ще кому стане в нагоді.

public class LargeMemoryHelper
{
    public static T[] GetArrayInAvailableMemory<T>(ref int _requiredSizeOfArray, ushort _percentOfDecrease=10)
    {
        T[] _retArray = new T[] { };

        if (_percentOfDecrease > 99) throw new Exception(
            "LargeMemoryHelper.GetArrayInAvailableMemory<>(): Percent of decrease should be less 100");

        try
        {
            double _sizeArray=_requiredSizeOfArray;

            while (_retArray.Length == 0)
            {
                _retArray = TryToCreateArray<T>(_requiredSizeOfArray);
                if (_retArray.Length == 0)
                {
                    _sizeArray = _sizeArray * (1 - (double)_percentOfDecrease / 100);
                    _requiredSizeOfArray = (int)Math.Truncate(_sizeArray);
                }
                if (_requiredSizeOfArray == 0)
                    break;
            }
        }
        catch(Exception _ex)
        {
            SysLog.SaveToLog("LargeMemoryHelper. Type:" + typeof(T).ToString() +
                " Required size of array "+ _requiredSizeOfArray.ToString() + "\r\n" + _ex.Message);
        }

        return _retArray;
    }

    private static T[] TryToCreateArray<T>(int _sizeOfArray)
    {
        T[] _retArray = new T[] { };
        try
        {
            _retArray = new T[_sizeOfArray];
        }
        catch (OutOfMemoryException) { }

        return _retArray;
    }
}

SysLog.SaveToLog() це логування при якихось інших непередбачених проблемах. Можете там юзати що вам самим треба.

Подякували: leofun011

2

Re: Великі одновимірні масиви і OutOfMemoryException

Ще за СРСР першим правилом програмування було "кожна програма намагається зайняти все доступне для неї місце в ОЗП". Ви, схоже, творче перенесли цю концепцію в .Net.
Вибачте, а навіщо віднімати всю пам'ять у інших, ні в чому не винних програм? Якщо програма завелика для ОЗУ - значить, треба писати в файли. Зрештою, з вашим кодом саме це і станеться - воно полізе в сторінковий файл.

Подякували: Engineer, leofun012

3 Востаннє редагувалося Engineer (19.05.2016 13:26:12)

Re: Великі одновимірні масиви і OutOfMemoryException

Іншим програмам там не місце :) Це робилося для специфічної служби, що працює на окремому виділеному сервері з 32 Гб ОП. Конкретно в моєму випадку я використовував масиви з типом byte  і максимальний розмір масиву в моєму класі не може перевищувати Int.MaxValue. Тобто 2 з хвостиком Гб. Вдавалося отримати масиви максимальним розміром до 1,8 Гб навіть при компіляції служби конкретно під x64. Складається враження, що .Net таки знає про цю концепцію радянських програмістів і намагається з нею боротися  :) .
А в загальному ви праві - бездумне використання таких штук запросто може повісити всю систему

4

Re: Великі одновимірні масиви і OutOfMemoryException

Прихований текст

знову всі ці T[] <T> http://не-дійсний-домен/oXubW/f709a35139.png

5

Re: Великі одновимірні масиви і OutOfMemoryException

FakiNyan написав:
Прихований текст

знову всі ці T[] <T> http://не-дійсний-домен/oXubW/f709a35139.png

Не любиш дженеріки ? може не правильно їх готуєш ?  %)

А якщо серйозно, то чим вони тут не так ? Як інакше зробити універсальний метод для створення масиву будь-якого типу ?

6 Востаннє редагувалося Engineer (19.05.2016 14:41:07)

Re: Великі одновимірні масиви і OutOfMemoryException

. дубль

7

Re: Великі одновимірні масиви і OutOfMemoryException

Якщо у системи 32 Гб ОП, а прогу скомпільовано під x64, то знайти безперервний шматок адресного простору на 2 Гб має бути легко. Навіть якщо менеджер пам'яті писали криворукі індуси і він не вміє боротися з фрагментацією. Може ви якихось флагів сумісності не виставили в PE-заголовку?

Подякували: koala, leofun01, Engineer3

8

Re: Великі одновимірні масиви і OutOfMemoryException

Torbins написав:

Якщо у системи 32 Гб ОП, а прогу скомпільовано під x64, то знайти безперервний шматок адресного простору на 2 Гб має бути легко. Навіть якщо менеджер пам'яті писали криворукі індуси і він не вміє боротися з фрагментацією. Може ви якихось флагів сумісності не виставили в PE-заголовку?

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

9

Re: Великі одновимірні масиви і OutOfMemoryException

З такою проблемою не стикався, але не думаю що може бути таке що менеджер пам'яті "не знайшов неперервну ділянку пам'яті", так як в крайньому випадку буде використаний файл підкачки. + якщо я правильно розумію, то та неперервна ділянка у віртуальній пам'яті не обов'язково мусить бути неперервною у фізичному пристрої.

Подякували: koala, Torbins2

10 Востаннє редагувалося raxp (21.05.2016 20:18:53)

Re: Великі одновимірні масиви і OutOfMemoryException

...проблема не нова, як казав koala якщо замало пам'ятi RAM, то використовують файл підкачки і відображення його в пам'ять програми. Для цього є API функції SetFilePointer(), CreateFileMapping(), MapViewOfFile(), UnmapViewOfFile():

https://msdn.microsoft.com/en-us/librar … 85%29.aspx
https://msdn.microsoft.com/en-us/librar … 85%29.aspx
https://msdn.microsoft.com/en-us/librar … 85%29.aspx
https://msdn.microsoft.com/en-us/librar … 85%29.aspx

Наведу приклад:

var
  FileHandle : THandle;
  Mapping : THandle;
  Data : Pointer;
  Offset : Int64Rec;
begin
  FileHandle := CreateFile(
    'c:\superram.myext', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
    CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE, 0
  );

  try
    { Задаємо розмір файлу в 4 GB }
    Int64(Offset) := $100000000;
    SetFilePointer( FileHandle, Offset.Lo, @Offset.Hi, FILE_BEGIN );
    SetEndOfFile( FileHandle );

    { Створюємо об'єкт відображення файлу }
    Mapping:= CreateFileMapping(FileHandle, nil, PAGE_READWRITE, 0, 0, nil );
    Data := MapViewOfFile(Mapping, FILE_MAP_WRITE, 0, 0, $40000000);
    try
      if not Assigned(Data) then Exit;
      { ...
        Тут робота з пам'яттю
        ... }
    finally
      UnmapViewOfFile(Data);
    end;
  finally
    CloseHandle(Mapping);
    CloseHandle(FileHandle);
  end;
Подякували: Engineer1

11

Re: Великі одновимірні масиви і OutOfMemoryException

@raxp
такі навороти з WinAPI в .Net не пройдуть, CLR не дає прямого доступу до памяті, а якщо дозволити unmanaged code, то вже можна і не на C# писати  :)

Подякували: leofun011

12

Re: Великі одновимірні масиви і OutOfMemoryException

Неважливо на чому писати.