text: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
  <html>
  <head>
  <title>Распределение памяти в Borland Delphi - проблемы и решения</title>
  <meta http-equiv="Content-Type" Content="text/html; charset=Windows-1251">
  <link rel="stylesheet" type="text/css" href="../../styles/styles.css">
  <link REV="made" href="mailto:vmoshninov@newmail.ru">
  <meta name="Author" content="Vasily V. Moshninov">
  <meta name="description" content="Распределение памяти в Borland Delphi - проблемы и решения">
  <script language="javascript" src='../links.js' type="text/javascript"></script>
  </head>
  <body>
  
  <a name="top"></a>
  <h1>Распределение памяти в Borland Delphi - проблемы и решения</h1>
  <div class=navbar>
  <a href="../index.html">главная</a> |
  <a href="index.html">статьи</a>
  </div>
  
  <div align=right>
  <code>Василий Мошнинов <a href="mailto:Vasily%20V%2E%20Moshninov%20<vmoshninov@newmail.ru>?subject=Articles">
  <img src="../../images/email.gif" border="0" alt="[vmoshninov@newmail.ru]" width="16" height="16" align="middle">vmoshninov@newmail.ru</a>
  </code></div>
  <hr color="#003399">
  
  <p class=plain>Недавно я получил письмо о том, что мой плагин <a target="_blank" href="http://plugring.farmanager.com/cgi-bin/downld.cgi?Draw=List&Select=PlugIn&SelectPlugIn=39">ProxyFtp</a>
  не может показать каталог, содержащий порядка 4000 файлов. Плагин
  выдавал сообщение "Out of memory". В памяти при этом ФАР занимал около 64Mb.
  Я решил проверить этот момент и, к сожалению, все мои опасения подтвердились.
  После анализа ситуации оказалось, что частый вызов борландовской функции
  ReallocMem приводил к большому объему расходуемой памяти и в последствии
  вообще переставал работать. Тогда я решил заменить все функции работы с
  памятью на соответствующие функции <a href="win32/memory management.html">Win32 API</a>, благо их использовалось у меня
  всего 3 штуки:
  <ul>
  <li>GetMem - выделить память
  <li>FreeMem - освободить память
  <li>ReallocMem - изменить объем выделенной памяти.
  </ul>
  <p class=plain>Просто отказаться от использования ReallocMem я не мог - она
  используется для создания списка <a href="../delphi/tpluginpanelitem.html">TPluginPanelItem</a>
  в функции <a href="../exported_functions/getfinddata.html">GetFindData</a>.
  С первыми двумя функциями проблем не было вообще никаких. Вот как они стали
  выглядеть у меня (функция RaiseLastWin32Error описана в модуле sysutils.pas):
  
  <pre class=code>procedure MemAlloc(var lpMem; Size: integer);
  begin
    pointer(lpMem):= pointer(<a href="win32/globalalloc.html">GlobalAlloc</a>(LPTR, Size));
    if pointer(lpMem) = nil then
      RaiseLastWin32Error;
  end; { procedure MemAlloc }
  
  procedure MemFree(var lpMem);
  begin
    pointer(lpMem):= pointer(<a href="win32/globalfree.html">GlobalFree</a>(HGLOBAL(lpMem)));
    if pointer(lpMem) &lt;&gt; nil then
      RaiseLastWin32Error;
  end; { procedure MemFree }</pre>
  
  <p class=plain>Небольшие затруднения возникли с функцией ReallocMem. Дело в
  том, что частый вызов <a href="win32/globalrealloc.html">GlobalRealloc</a> из Win32 API также приводил к ошибке
  (что-то про "Insufficient storage...") Тогда я, основываясь на потребностях
  плагина, сделал эту функцию как вызов двух предыдущих с некоторыми
  ограничениями.
  
  <p class=plain>Функция первый раз возвращает требуемое количество памяти. Во
  все следующие вызовы память резервируется "с запасом" - кратно константе
  MEM_DELTA. Это позволило избежать частых распределений памяти при запросах
  небольшими порциями:
  
  <pre class=code>procedure MemRealloc(var lpMem; uSize: integer);
  const
    MEM_DELTA = $2000; // 8192 bytes
  var
    P: pointer;
    Size: integer;
  begin
    if uSize = 0 then
      MemFree(lpMem)
    else if pointer(lpMem) = nil then
  // Первый раз вернем столько, сколько запросили
      MemAlloc(lpMem, uSize)
    else
    begin
  // Требуемый размер, выровненный на границу MEM_DELTA
      uSize := (uSize + (MEM_DELTA - 1)) and not (MEM_DELTA - 1);
  // Текущий размер захваченной памяти
      Size:= GlobalSize(HGLOBAL(lpMem));
      if (uSize  &gt; Size) or ((Size - uSize) &gt; MEM_DELTA) then
  // Запросили больше распределенного размера или уменьшили на величину, большую
  // MEM_DELTA
      begin
        MemAlloc(P, uSize);
        if Size &gt; uSize then
          Size:= uSize;
        Move(pointer(lpMem)^, P^, Size);
        MemFree(lpMem);
        pointer(lpMem):= P;
      end;
    end; { else }
  end; { procedure MemRealloc }</pre>
  
  <p class=plain>Когда я заменил все вызовы стандартных функций работы с памятью
  на эти, FAR смог спокойно показать 4600 файлов (раньше падал на 3800), при этом
  занял в оперативной памяти около 10 Mb (против начальных 64 Mb) и скорость
  построения списка <a href="../delphi/tpluginpanelitem.html">TPluginPanelItem</a>
  возросла до ~3 сек (против ~30 сек в начале).
  
  <p class=plain>Естественно, в каждом конкретном случае надо смотреть величину
  MEM_DELTA. Плюс накладывается ограничение на размер в 1 Gb, вместо 2 Gb.
  Однако, судя по работе плагина - оно того стоило :))
  
  <p class=plain>Новые функции работы с памятью задействованы в
  ProxyFtp 1.6.r3 и в ProxyNews 1.0 Если ваш плагин не может показать каталог,
  выдавая ошибку "Out of memory" - проверьте его версию и в случае
  необходимости - обновите:
  <ul>
  <li><a target="_blank" href="http://proxykit.newmail.ru">http://proxykit.newmail.ru</a>
  <li><a target="_blank" href="http://proxykit.narod.ru">http://proxykit.narod.ru</a>
  </ul>
  
  
  <div align=right><code>
  <br>&nbsp;<br>
  14.04.2000
  </code></div>
  <div class=seecont><a href="#top">наверх</a></div>
  </body>
  </html>
  