This is NOT directly related to the 10 GB of RAM used by the Gateway hosting 2.000 clients, but ... the Memory usage you see in your short Gateway Test (first video) after opening and closing 5.000 connections is probably caused by RAM that remains allocated for the Stack used by RTC Worker Threads. This is normal, because a Thread can NOT work without a Stack. It also won't result in a Server using GBs or RAM, since a Thread only uses 1 MB of RAM by default for Stack (and can also be reduced with a compiler directive).
Anyway ... if you run the "Multi-Threaded Memory Manager Test" (see the source code below), you can reproduce the behavior you've seen in your short Gateway Test. Simply open a number of Threads (click the "Open 1" button multiple times), wait a while for them to allocate RAM (you will see the "Used ... KB RAM" value going up), then click the "Stop All" button and wait for the Threads to release the RAM used for dynamic arrays.
You should see that, even after memory used for dynamic arrays has been released (the label should display "Using 0 KB RAM"), the process is still using several MB of RAM. The amount would depend on the number of Threads you leave open. In my test with 50 threads, it was roughly 50 MB. Memory usage is going to reach a low point again ONLY after you close all the Threads by clicking the "Close 1" button until "Open 0 Threads" and "Used 0 KB RAM" is displayed on the Form.
Here's the source code of my relatively simple "Multi-Threaded Memory Manager test" ...
PAS file ...unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Open: TButton;
Close: TButton;
lMemUse: TLabel;
Timer1: TTimer;
Start: TButton;
Stop: TButton;
lRun: TLabel;
lWork: TLabel;
procedure OpenClick(Sender: TObject);
procedure CloseClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure StartClick(Sender: TObject);
procedure StopClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
TEST_SIZE=1*1000; // 1.000 arrays per Thread
TEST_MAX=10*1000*1000; // up to 10 MB per Thread
type
TByteArr=array of byte;
TThr=class(TThread)
public
arr:array[0..TEST_SIZE] of TByteArr;
memuse:longint;
run,work:boolean;
procedure Execute; override;
end;
procedure TThr.Execute;
var
i,olen,nlen:integer;
function doRun:boolean;
begin
result:=run;
end;
begin
i:=0;memuse:=0;
while doRun do
begin
if work or (memuse>0) then
begin
olen:=length(arr[i]);
SetLength(arr[i],0);
Dec(memuse,olen);
if work then
begin
case random(6) of
0: nlen:=random(100)+1;
1: nlen:=random(1000)+100;
2: nlen:=random(10000)+1000;
3: nlen:=random(100000)+10000;
4: nlen:=random(100000)+30000;
else nlen:=0;
end;
end
else
nlen:=0;
if (nlen>0) and (memuse<TEST_MAX) then
begin
if (nlen+memuse>TEST_MAX) then nlen:=TEST_MAX-memuse;
SetLength(arr[i],nlen);
Inc(memuse,nlen);
end;
end;
if(i<TEST_SIZE) then
Inc(i)
else if Application.Terminated then
break
else
begin
i:=0;
Sleep(0);
end;
end;
for i:=0 to TEST_SIZE do Setlength(arr[i],0);
end;
var
thr:array of TThr;
procedure TForm1.OpenClick(Sender: TObject);
var
i:integer;
begin
randomize;
i:=length(thr);
SetLength(thr,i+1);
thr[i]:=TThr.Create(true);
thr[i].FreeOnTerminate:=true;
thr[i].memuse:=0;
thr[i].work:=True;
thr[i].run:=True;
thr[i].Suspended:=False;
end;
procedure TForm1.CloseClick(Sender: TObject);
var
i:integer;
mt:TThr;
begin
i:=length(thr);
if i>0 then
begin
Dec(i);
mt:=thr[i];
thr[i]:=nil;
mt.run:=False;
SetLength(thr,i);
end;
end;
procedure TForm1.StartClick(Sender: TObject);
var
i:integer;
begin
for i:=0 to length(thr)-1 do thr[i].work:=True;
end;
procedure TForm1.StopClick(Sender: TObject);
var
i:integer;
begin
for i:=0 to length(thr)-1 do thr[i].work:=False;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
i,sumuse,srun,swork:integer;
begin
sumuse:=0;srun:=0;swork:=0;
for i:=0 to length(thr)-1 do
begin
Inc(sumuse,thr[i].memuse);
if thr[i].run then Inc(srun);
if thr[i].work then Inc(swork);
end;
lRun.Caption:='Open '+IntToStr(srun)+' Threads';
lWork.Caption:='Working '+IntToStr(swork)+' Threads';
lMemUse.Caption:='Using '+IntToStr(round(sumuse/1024))+' KB RAM';
end;
end.
DFM file ...object Form1: TForm1
Left = 317
Top = 193
Caption = 'Form1'
ClientHeight = 193
ClientWidth = 205
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object lMemUse: TLabel
Left = 8
Top = 160
Width = 82
Height = 16
Caption = 'Using 0 Bytes'
end
object lRun: TLabel
Left = 8
Top = 112
Width = 97
Height = 16
Caption = 'Open 0 Threads'
end
object lWork: TLabel
Left = 8
Top = 136
Width = 114
Height = 16
Caption = 'Working 0 Threads'
end
object Open: TButton
Left = 8
Top = 8
Width = 85
Height = 41
Caption = 'Open 1'
TabOrder = 0
OnClick = OpenClick
end
object Close: TButton
Left = 99
Top = 8
Width = 89
Height = 41
Caption = 'Close 1'
TabOrder = 1
OnClick = CloseClick
end
object Start: TButton
Left = 8
Top = 52
Width = 85
Height = 41
Caption = 'Start All'
TabOrder = 2
OnClick = StartClick
end
object Stop: TButton
Left = 96
Top = 52
Width = 89
Height = 41
Caption = 'Stop All'
TabOrder = 3
OnClick = StopClick
end
object Timer1: TTimer
OnTimer = Timer1Timer
Left = 148
Top = 104
end
end