1

我正在使用 Delphi 10.3.1,并为在 Apache 2.49 下的 Linux Ubuntu 18.04 上运行的 RAD 服务器资源/端点生成了一个示例应用程序。我的应用程序 ProcessArray 只创建了两个动态数组,其中组件是 12 个数字字段的记录。这些动态数组的大小是在调用端点时在运行时定义的。在执行期间,此应用程序按预期分配 RAM 内存,但是当它完成时,它不会释放 RAM!

如果连续调用数组大小​​为 20 或 3000 万,情况更糟,因为在完成运行后,每次调用仍然分配大约 1GB 的空间,导致使用 SWAP 区域使机器变得非常缓慢和不稳定。

取决于调用次数,它不能再运行并返回“内存不足”错误消息。

部署到在 Windows 上运行的 RAD 服务器时不会出现此问题。

查看我的代码:

unit UntStru;

interface

Uses
       System.Threading,
       System.Classes;
Type
   TRec = record
         F1    : Integer;
         F2    : String;
         F5    : Real;
         F9    : Real;
         F10   : Real;
         // other Integer and Real fields
   End;

   TArrayRec        =  array of TRec;

   Function Process_array       (Var pArray1,pArray2   : TArrayRec; Const pfirst, plast : integer) : iTask;
   Function Process_arrayThread (Const pSize, pThreads : integer) : Integer;

implementation

Uses
    System.SysUtils,System.Diagnostics;

  Function Process_array    (Var pArray1,pArray2 : TArrayRec; Const pfirst, plast : integer) : iTask;
  var
      indx                : integer;
      larray1, larray2    : TArrayRec;
  begin
         // assigns var parameter array to local variable array because Ttask does not recognize var parameters
         lArray1 := pArray1;
         lArray2 := pArray2;

         // creates a Task to process arrays positions from pfirst to plast
         Result := TTask.Create
                   (
                      procedure
                       var
                          indx : integer;
                       begin
                             // scans array pArray from position pFirst through pLast
                             for indx := pFirst to pLast do
                             begin
                                   with lArray1[indx] do
                                   begin
                                        F1  := indx;
                                        F2  := 'Element  ' + indx.ToString;
                                        F5  := 2.5 * indx;
                                        F9  := 1.3 * indx;
                                        F10 := pLast;
                                   end;

                                   lArray2[indx] := lArray1[indx];
                             end;
                            // Just to assure that memory is being deallocated by the application
                            // This does not made any difference in deallocatin issue on Linux
                         {   Setlength(larray1,0);
                            Setlength(larray2,0);
                            FreeAndNil(larray1);
                            FreeAndNil(larray2);
                         }
                       end
                   );
  end;



  Function  Process_arrayThread (Const pSize, pThreads : integer) : Integer;
  var
      lvettask                  : array of iTask;
      lparray1, lparray2        : TArrayRec;
      ind, lsize, lstart, lend  : Integer;
   begin
        Try
            // array size in million
            lsize := 1000000 * pSize;

            // creates larray1 and set array size at once
            setlength(lparray1, lSize);

            //creates larray2 incrementing one by one
            for ind := 0  to lsize-1 do
            begin
                  setlength(lparray2,ind+1);
            end;

            // creates the array if iTasks according to number of Threads "pThreads"
            setlength(lvettask,pThreads);

            // creates slice size of array to be processed by each Thread
            lsize := lsize div pthreads;

            // creates each iTask on the array of iTasks
            for ind := 0 to pThreads-1 do
            begin
                 lstart := ind * lsize ;
                 lend   := (ind + 1) * lsize - 1 ;

                 lvettask[ind] := Process_array(lpArray1, lpArray2,lstart, lend);
            end;

            // starts each iTaks from array of iTasks
            for ind := 0 to pThreads-1 do
                lvetTask[ind].Start;

            // waits all iTasks been concluded
            TTask.WaitForAll(lvettask);
        Finally
            //
        End;
   end;

end.

资源/端点代码是:

unit UntProcessArray;    
// EMS Resource Module

interface    
uses
  System.SysUtils, System.Classes, System.JSON,
  EMS.Services, EMS.ResourceAPI, EMS.ResourceTypes;

type
  [ResourceName('ProcessArray')]
  TProcessArrayResource1 = class(TDataModule)
  published
    [ResourceSuffix('{item}')]
    procedure PutItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
  end;

implementation
{%CLASSGROUP 'System.Classes.TPersistent'}  {$R *.dfm}
Uses
   UntStru,System.Diagnostics;

procedure TProcessArrayResource1.PutItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
    LItem     : string;
    Linteger  : integer;
begin
      TRY
            LItem     := ARequest.Params.Values['item'];
            Linteger  := StrToint(Litem);

            // calls processing major function creating 4 threads
            Process_ArrayThread(Linteger,4);   

            AResponse.Body.SetValue(TJSONArray.Create('Message :','Concluded sucessfully'),True);
     Except
            on E:Exception do
            begin
                  AResponse.Body.SetValue(TJSONArray.Create('Mensagem', E.Message), True);
            end;
     End;
end;

procedure Register;
begin
  RegisterResource(TypeInfo(TProcessArrayResource1));
end;

initialization
  Register;
end.

用法: http://myLinuxUbuntuServer/ems-server/ProcessArray/30 用于 30 百万数组大小

我仍然怀疑这是否真的与在 Ubuntu Linux/Apache 上运行的 RAD Server 10.3.1 相关,或者我的代码是否存在缺失或错误。

这是否也发生在 Delphi 10.3.2 新版本上?(如果有人安装了这个版本并且可以在我继续升级之前为我测试,我将不胜感激)

这会发生在 Ubuntu 以外的其他 Linux 发行版上吗?

我非常感谢你们的帮助!谢谢。

Edition 1:  
Environment info : 
IDE : Delphi Tokyo 10.3.1 Update 1 (version 26.0.33219.4899 ) 
Server : Linux Ubuntu 18.04.2 LTS (64 bits) - 8GB RAM 8GB SWAP 
Kernel 4.15.0-1045-aws     
Web Server : Apache 2.4.29 RAD
Server bpl application compiled to Linux 64-bits

Linux 服务器 htop 命令的屏幕截图 在第一次执行之前

在此处输入图像描述

执行 6次 http://myLinuxUbuntuServer/ems-server/ProcessArray/20后 Linux 服务器 htop 的屏幕截图 在此处输入图像描述

请注意,即使在执行完成后,RAM 仍会继续分配 5.30GB。对该端点的进一步调用将从 5.30GB RAM 开始,很快就会达到总 RAM 大小(8GB),产生“内存不足”错误,或者将使用 SWAP 区域,机器将变得不稳定和缓慢!

添加我的 .dproj 文件:Proj_ProcessArray.dproj

package Proj_ProcessArray;

{$R *.res}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO OFF}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS OFF}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO OFF}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE RELEASE}
{$ENDIF IMPLICITBUILDING}
{$RUNONLY}
{$IMPLICITBUILD ON}

requires
  rtl,
  emsserverapi;

contains
  UntStru in 'UntStru.pas',
  UntProcessArray in 'UntProcessArray.pas' {ProcessArrayResource1: TDataModule};

end.

为 Linux 64 添加一个控制台应用程序 ProcessArrayConsole,它使用与上面单元 UntStru 发布的相同单元

program ProcessArrayConsole;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  UntStru in 'UntStru.pas';

var
   lsize   : integer;
   lstraux : string;
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }

    // get size of array in millions
     lsize := ParamStr(1).tointeger ;

    // call Process_ArrayThread using 4 threads
    Process_arrayThread(lsize,4);

    lstraux := FloatToStrF(lsize * 1000000,ffnumber,10,0);

    Writeln;
    Writeln('Concluded to process two arrays of ' + lstraux + ' positions.');
    Writeln;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

我在同一个 Linux 服务器上运行了这个控制台应用程序,调用了几次 ./ProcessArrayConsole 30 并且 RAM 在执行后完全释放了!!。

因此,似乎只有当我使用 EMS Apache 模块或 EMSDevServer(独立)时才会出现此释放问题。

唯一的例外发生在我同时运行 3 个 3000 万大小的调用时。在这种情况下,总共消耗了 8GB RAM,并开始使用 SWAP 区域,但 achie 变得非常慢,三个过程需要很长时间才能结束。(12 分钟后仍在运行)。

4

2 回答 2

2

在执行期间,此应用程序按预期分配 RAM 内存,但是当它完成时,它不会释放 RAM!

Delphi 的内存管理器以块的形式从操作系统分配内存,并缓存释放的内存以供以后分配重用,它不会返回给操作系统。因此,如果您通过从操作系统分配内存的方式来衡量泄漏,这不是诊断泄漏的方法。请改用内存管理器自己的内置泄漏报告功能。

话虽如此,您Process_arrayThread()返回一个TStringList对象作为输出,但我没有看到您释放它。

于 2019-09-11T05:52:24.330 回答
0

您必须微调您的 Apache 服务器。

请看一下下面的讨论(它适用于 PHP,但适用于任何环境):

https://serverfault.com/questions/68410/a-question-of-memory-for-an-apache-server/68427#68427

于 2019-09-12T15:30:24.757 回答