<!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>
