Не освобождается ли память для приложений Delphi, работающих на Windows Server 2008 (sp1)? - PullRequest
3 голосов
/ 23 апреля 2009

У нас есть приложение D2007, объем памяти которого постоянно растет при работе в Windows Server 2008 (x64, sp1).
Он работает нормально на Windows Server 2003 (x32 или x64), XP и т. Д., Где он идет вверх и вниз, как и ожидалось.
Мы пробовали использовать прилагаемый диспетчер памяти или последнюю версию FastMM4 4.92 с теми же результатами.

Кто-нибудь пытался отслеживать использование памяти любого приложения Delphi на Win2008 и подтвердит?
Или есть какие-либо подсказки?

Precisions:
- нет утечек памяти в здравом смысле (и да, я хорошо знаком с FastMM и др.)
- использование памяти контролировалось с помощью Process Explorer; Виртуальная память (Private Bytes) и физическая память (WorkingSet Private) растут на Win2008
- потребление памяти все еще росло, даже когда было давление памяти. (вот как мы пришли к расследованию, поскольку это вызвало сбой, но только на коробках Win2008)

Обновление : код // ** перераспределен ** // намного проще, чем наше приложение, но демонстрирует то же поведение.
Создание списка из 10 000 000 объектов, затем 10 000 000 интерфейсов, выполненных в 2 раза, увеличивает используемую память на ~ 60 МБ и примерно на 300 МБ больше для еще 100 выполнений в Windows Server 2008, но просто возвращается туда, где она была в XP. Если вы запускаете несколько экземпляров, память не освобождается для запуска других экземпляров. Вместо этого файл подкачки увеличивается, и сервер сканирует ...

Обновление 2 : см. Отчет о контроле качества 73347
После дальнейшего исследования мы отследили его до критических разделов , как показано в коде ниже.
Поместите этот код в простое приложение VCL с помощью кнопки. И монитор с помощью Process Explorer:
он начинается с ~ 2,6 МБ и после 5 запусков (щелкает по кнопке) остается на уровне ~ 118,6 МБ.
116 МБ потеряно в 5 казнях.

//***********************
const
  CS_NUMBER = 10000000;
type
  TCSArray = Array[1..CS_NUMBER] of TRTLCriticalSection;
  PCSArray = ^TCSArray;

procedure TestStatic;
var
  csArray: PCSArray;
  idx: Integer;
begin
  New(csArray);

  for idx := 1 to length(csArray^) do
    InitializeCriticalSection(csArray^[idx]);

  for idx := 1 to length(csArray^) do
      DeleteCriticalSection(csArray^[idx]);

  Dispose(csArray);
end;

procedure TestDynamic(const Number: Integer);
var
  csArray: array of TRTLCriticalSection;
  idx: Integer;
begin
  SetLength(csArray, Number);

  for idx := Low(csArray) to High(csArray) do
    InitializeCriticalSection(csArray[idx]);

  for idx := Low(csArray) to High(csArray) do
      DeleteCriticalSection(csArray[idx]);
end;

procedure TForm4.Button1Click(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := True;
  TestStatic;
  TestDynamic(CS_NUMBER);
end;

Ответы [ 8 ]

3 голосов
/ 06 августа 2011

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

Решение, если вы хотите создать много критических разделов, не ощущая потери памяти, состоит в исправлении кода VCL для замены вызовов на InitializeCriticalSection вызовами на InitializeCriticalSectionEx и передайте ему флаг CRITICAL_SECTION_NO_DEBUG_INFO, чтобы избежать создания структуры отладки.

3 голосов
/ 23 апреля 2009

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

1 голос
/ 05 августа 2011

Я сделал этот код, чтобы исправить эту проблему в моих приложениях. Это тот же случай FastCode, чтобы запустить исправление, вы должны поместить модуль в качестве первого модуля вашего проекта. Как uRedirecionamentos в этом случае: enter image description here

unit uCriticalSectionFix;
// By Rodrigo F. Rezino - rodrigofrezino@gmail.com

interface

uses
  Windows;

implementation

uses
  SyncObjs, SysUtils;

type
  InitializeCriticalSectionExProc = function(var lpCriticalSection: TRTLCriticalSection; dwSpinCount: DWORD; Flags: DWORD): BOOL; stdcall;

var
  IsNewerThenXP: Boolean;
  InitializeCriticalSectionEx: InitializeCriticalSectionExProc;

type
  PJump = ^TJump;
  TJump = packed record
    OpCode: Byte;
    Distance: Pointer;
  end;

  TCriticalSectionHack = class(TSynchroObject)
  protected
    FSection: TRTLCriticalSection;
  public
    constructor Create;
  end;

function GetMethodAddress(AStub: Pointer): Pointer;
const
  CALL_OPCODE = $E8;
begin
  if PBYTE(AStub)^ = CALL_OPCODE then
  begin
    Inc(Integer(AStub));
    Result := Pointer(Integer(AStub) + SizeOf(Pointer) + PInteger(AStub)^);
  end
  else
    Result := nil;
end;

procedure AddressPatch(const ASource, ADestination: Pointer);
const
  JMP_OPCODE = $E9;
  SIZE = SizeOf(TJump);
var
  NewJump: PJump;
  OldProtect: Cardinal;
begin
  if VirtualProtect(ASource, SIZE, PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    NewJump := PJump(ASource);
    NewJump.OpCode := JMP_OPCODE;
    NewJump.Distance := Pointer(Integer(ADestination) - Integer(ASource) - 5);

    FlushInstructionCache(GetCurrentProcess, ASource, SizeOf(TJump));
    VirtualProtect(ASource, SIZE, OldProtect, @OldProtect);
  end;
end;

procedure OldCriticalSectionMethod;
asm
  call TCriticalSection.Create;
end;

{ TCriticalSectionHack }

const
  CRITICAL_SECTION_NO_DEBUG_INFO = $01000000;
  NEW_THEN_XP = 6;

constructor TCriticalSectionHack.Create;
begin
  inherited Create;
  if IsNewerThenXP then
    InitializeCriticalSectionEx(FSection, 0, CRITICAL_SECTION_NO_DEBUG_INFO)
  else
    InitializeCriticalSection(FSection);
end;

procedure AdjustMethod;
var
  LKernel32: HModule;
begin
  if IsNewerThenXP then
  begin
    LKernel32 := LoadLibrary('kernel32.dll');
    @InitializeCriticalSectionEx := GetProcAddress(LKernel32, 'InitializeCriticalSectionEx');
  end;
end;

initialization
  AddressPatch(GetMethodAddress(@OldCriticalSectionMethod), @TCriticalSectionHack.Create);
  IsNewerThenXP := CheckWin32Version(NEW_THEN_XP, 0);
  AdjustMethod;


end.
1 голос
/ 25 апреля 2009

Проверьте, есть ли у вас эта проблема (это другая проблема, не связанная с той, о которой я упоминал в комментариях к вашему вопросу).

1 голос
/ 23 апреля 2009

Вы имеете в виду личные байты, виртуальный размер или рабочий набор? Запустите Process Explorer из SysInternals , чтобы отслеживать память для лучшего понимания происходящего.

У меня нет особого опыта в этом (хотя я использую 2008 x64 SP1, так что могу его протестировать), но я собираюсь предложить вам создать тестовое приложение, которое выделяет кучу памяти, а затем освобождает ее. Запустите Process Explorer из SysInternals для мониторинга памяти.

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

Если это не помогло, попробуйте другой диспетчер памяти. Может быть, это делает FastMM.

1 голос
/ 23 апреля 2009

Вы включили FastMM с полным режимом отладки? Просто включите модуль FastMM4 прямо в ваш проект и установите

ReportMemoryLeaksOnShutdown := True

Если ничего не сообщается, возможно, все нормально освобождается при выходе из программы (возможно из-за подсчета ссылок) Вы можете использовать AQTime для мониторинга памяти в режиме реального времени. С помощью этого приложения вы можете увидеть байты "подсчета" для каждого имени класса и для остальной части используемой памяти. Может быть, вы можете увидеть, кто использует память. Для этой работы достаточно демо-версии, ограниченной по времени.

0 голосов
/ 26 апреля 2009

В дополнение к Александру, обычно это называется «фрагментация кучи».

Обратите внимание, что FastMM должен быть более гибким и быстрым в целом, но если исходное приложение было настроено для memmanager D7, FastMM может действительно работать хуже.

0 голосов
/ 24 апреля 2009

Что ж, использование памяти может увеличиться, даже если в вашем приложении нет утечки памяти. В тех случаях есть вероятность, что у вас есть утечка другого ресурса. Например, если ваш код выделяет, скажем, растровое изображение и хотя он освобождает все объекты, но ему удается забыть о завершении какой-то HBITMAP.

FastMM сообщит вам, что в вашем приложении нет утечки памяти, поскольку вы освободили все свои объекты и данные. Но у вас все еще есть утечки других типов ресурсов (в моем примере - объекты GDI). Утечка других типов ресурсов также может повлиять на вашу память.

Я предлагаю вам попробовать другой инструмент, который проверяет не только утечки памяти, но и утечки других типов. Я думаю, что AQTime способен сделать это, но я не уверен.

Другая возможная причина такого поведения - фрагментация памяти. Предположим, вы выделили 2000 объектов размером 1 Мб (давайте на минуту забудем о накладных расходах MM и наличии других объектов в пользовательском пространстве). Теперь у вас есть полные 2 ГБ занятой памяти. Теперь предположим, что вы освобождаете все четные объекты, поэтому теперь у вас есть «очищенное» пространство памяти, в котором 1 МБ занята и свободные блоки смешаны. Хотя теперь у вас есть 1 ГБ свободной памяти, но вы не можете выделить память для любого 2 МБ-объекта, поскольку максимальный размер свободного блока составляет всего 1 МБ (но у вас есть 1000 таких блоков;)). Если менеджер памяти использовал блоки размером более 1 Мб для ваших объектов, то он не сможет освободить блоки памяти обратно в ОС, когда вы освободили четные объекты:

[ [busy] [free] [busy] [free] [busy] [free] ]
[ [busy] [free] [busy] [free] [busy] [free] ]...

Эти большие [...] блоки наполовину заняты, поэтому ММ не может передать их ОС. Если вы попросите другой блок, который составляет> 1 МБ, тогда MM потребуется выделить еще один блок из ОС:

[ [busy] [free] [busy] [free] [busy] [free] ]
[ [busy] [free] [busy] [free] [busy] [free] ]...
[ [your-new-object] [free.................] ]

Обратите внимание, что это всего лишь примеры увеличения использования памяти, хотя у вас нет утечки памяти. Я не говорю, что у вас есть ТОЧНАЯ ситуация: D

...