Найти самый большой свободный блок памяти - PullRequest
3 голосов
/ 15 февраля 2010

Иногда возникает проблема с нехваткой памяти, когда она фрагментирована.

Можно ли найти самый большой свободный блок памяти? Я использую Delphi 2007 с FastMM. Разработка под Windows XP, запуск приложения на Windows 2003.

Привет

EDIT: Я мог бы добавить информацию о том, что приложение работает на сервере с 32 ГБ памяти на Windows Server 2003 x64. Но приложение является 32-битным приложением, поэтому теоретический максимальный объем выделенной памяти для каждого экземпляра составляет 2 ГБ. Многие экземпляры запускаются одновременно. Я не думаю, что это общая физическая память, которая мало. Я предполагаю, что при запуске приложение получило 32-битную виртуальную память. Это может быть слишком фрагментировано во время выполнения.

Я также нашел метод FastGetHeapStatus, который возвращает THeapStatus с некоторыми полями для свободной памяти. Может быть, я мог бы использовать их.

EDIT2: Я нашел это Как получить самый большой доступный блок продолжения памяти . Код C, но, возможно, его можно перевести на Delphi.

Ответы [ 3 ]

6 голосов
/ 15 февраля 2010

Нет, это «maxavail» в старом Turbo Pascal, часто запрашиваемая функция, но, к сожалению, это бесполезная концепция в многопользовательской, многозадачной среде

Администратор heapmanager может знать самый большой блок в памяти, который он поддерживает сам, но обычно он будет маленьким, поскольку большие чанки выделяются непосредственно из окон Windows (и возвращаются в них).

И схемы постепенной попытки выделения больших блоков потерпят неудачу, потому что ОС будет удовлетворять запросы, даже если это означает подкачку на диск (что вам не нужно). То же самое для уловок, которые пытаются добывать такие значения с помощью вызовов Windows API.

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

Если ваше приложение действительно зависит от этого, задайте для него конфигурационный параметр (объем памяти, выделяемый при запуске для чего-либо) с безопасным (небольшим) значением по умолчанию. Если это ДЕЙСТВИТЕЛЬНО важно, предоставьте пользователю это во время установки

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

4 голосов
/ 01 марта 2010

Это перевод на Delphi-код, который вы хотели:

function GetLargestFreeMemRegion(var AAddressOfLargest: pointer): LongWord;
var
  Si: TSystemInfo;
  P, dwRet: LongWord;
  Mbi: TMemoryBasicInformation;
begin
  Result := 0;
  AAddressOfLargest := nil;
  GetSystemInfo(Si);
  P := 0;
  while P < LongWord(Si.lpMaximumApplicationAddress) do begin
    dwRet := VirtualQuery(pointer(P), Mbi, SizeOf(Mbi));
    if (dwRet > 0) and (Mbi.State and MEM_FREE <> 0) then begin
      if Result < Mbi.RegionSize then begin
        Result := Mbi.RegionSize;
        AAddressOfLargest := Mbi.BaseAddress;
      end;
      Inc(P, Mbi.RegionSize);
    end else
      Inc(P, Si.dwPageSize);
  end;
end;

Вы можете использовать это так:

procedure TForm1.FormCreate(Sender: TObject);
var
  BaseAddr: pointer;
  MemSize: LongWord;
begin
  MemSize := GetLargestFreeMemRegion(BaseAddr);
  // allocate dynamic array of this size
  SetLength(fArrayOfBytes, MemSize - 16);

  Caption := Format('Largest address block: %u at %p; dynamic array at %p',
    [MemSize, BaseAddr, pointer(@fArrayOfBytes[0])]);
end;

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

3 голосов
/ 15 февраля 2010

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

Однако для большинства опций код вашего приложения должен быть осведомлен о стратегии управления памятью на каком-то уровне. Я не верю, что есть быстрое решение этой проблемы - вы, вероятно, готовы к достаточно серьезной операции, чтобы ее решить. Ни один из этих вариантов не прост в реализации, вам придется найти тот, который с наибольшей вероятностью сработает в вашем конкретном случае.

Основные параметры, которые я вижу, это: пользовательские распределители памяти, что-то, связанное с AWE (см. Ниже) или перестройка стратегии выделения памяти в приложении.

Вариант 1: Пользовательские распределители памяти

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

  • Создайте распределитель памяти с механизмом, который пытается объединить смежные свободные блоки в один больший блок (вы можете выполнить это как часть попытки восстановления после неудачного выделения памяти). Это может позволить вам прозрачно управлять памятью без ведома приложения. Реализация этого была бы сложной и технической, но, вероятно, выполнимой.

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

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

Вариант 2: PAE

Windows поддерживает средства для непосредственного управления MMU, и есть несколько возможностей, которые могут применяться к вашему приложению. Это определенно потребует явной архитектурной поддержки от вашего приложения, но предлагает возможность использования пула памяти, который намного больше, чем 2 ГБ.

В серверных версиях Windows посмотрите на PAE , который поддерживается API , которые позволяют вам вручную манипулировать системным MMU и повторно отображать фрагменты памяти. Это может быть полезным для вас одним из двух способов

  • Вы можете построить менеджер для структуры данных таким образом, чтобы использовать этот механизм как неотъемлемую часть управления данными.

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

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

Однако механизм прокси такого рода может означать, что эта подсистема может быть относительно прозрачной для клиентов. Помимо накладных расходов на управление косвенным обращением и оверлеями (что может или не может быть существенной проблемой), прокси могут быть практически неотличимы от исходного API. Этот тип подхода лучше всего подойдет для относительно небольшого числа больших и тяжеловесных объектов с минимальным присоединением - каноническим приложением для этого является кэширование на диске. Прокси-серверы должны были бы оставаться в фиксированном месте в памяти, чтобы к более крупным объектам обращались через механизм наложения. YMMV.

Вариант 3. Исправить проблему в источнике

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...