连接关闭后如何释放DataSnap内存?

时间:2013-07-19 14:14:18

标签: delphi memory-leaks delphi-xe2 datasnap

我有一个TCP / IP DataSnap服务器作为服务运行[基于会话的LifeCycle],它不断地咀嚼内存,即使没有连接也永远不会回到起始内存大小。

为了消除我的代码作为罪魁祸首,我建立了一个基本的TCP / IP DataSnap服务器,作为VCL [基于会话的LifeCycle]运行,服务于一个服务器方法类[TDSServerModule],它只包含使用本机数据的基本数学函数类型[没有要创建或免费的对象]。

当我使用非常瘦的客户端连接到所述DataSnap服务器时,我得到相同的结果。 内存使用率随着每个连接不断增长,并且在从客户端执行服务器端方法时偶尔会增长。连接关闭后,DataSnap Server永远不会降低其内存使用率[即使在8小时内没有连接的情况下运行]。

有关为何会出现这种情况的建议,或者更重要的是如何减少它?

我使用的是RAD Studio XE2 Update 4 HotFix 1。

3 个答案:

答案 0 :(得分:2)

让我引用一篇关于DataSnap的“必读”文章。这是关于XE3但我希望这里的代码也适用于XE2。

内存消耗

我观察到的一个问题与内存消耗有关。如果调用的方法绝对没有任何内容,为什么Datasnap服务器会消耗这么多内存?

也许我不知道如何解释,但我会尝试。基本上,DataSnap为它接收的每个HTTP连接创建一个会话。这个会话将在20分钟后被销毁,换句话说,在测试的前20分钟内,内存消耗量只会增加,之后它会有稳定的趋势。我真的不知道为什么Datasnap会这样做。在REST应用程序中,我在使用默认配置的这些会话中看不到多少意义。当然,会话可能会有所帮助,但我无法理解为什么它是默认配置。实际上,DataSnap没有配置。看起来你只需要使用这个会话控件,而不能选择其他(没有文档)。 MORMot框架也有一个会话控制,但它是可配置的,不会占用太多内存。

无论如何,有一种方法可以解决这个问题。 Daniele Teti在他的博客上写了一篇文章,看一看。我将在这里展示的解决方案由他放在他的博客上。谢谢Daniele。

uses System.StrUtils, DataSnap.DSSession, Data.DBXPlatform;

function TServerMethods1.HelloWorld: String;
 begin

 Result := 'Hello World';
 GetInvocationMetaData.CloseSession := True;
end;

运行此方法后,会话将关闭,内存消耗将降低。当然,仍然存在创建和销毁此会话的开销。

因此,对于您来说,最好的方法是使用显式内存清理来结束每个服务器方法,如果在XE2中可行的话。然后,您最好再次阅读这些文章,为未来的可扩展性挑战做好准备。

答案 1 :(得分:1)

您应该检查servercontainer上TDSServerclass组件的Lifecycle属性。它提供了一种确定会话处理方式的方法。它默认为会话。将其设置为invokation将在每次调用(invokation)后释放会话。这当然意味着你没有国家。但这在典型的REST服务器中可能会出现问题。

如果你仍然有内存消耗增长。将以下行放在您的dpr单元中。 ReportMemoryLeaksOnShutdown:= True;
然后,您的应用程序将向您显示关闭datasnap服务器时的内存泄漏。

答案 2 :(得分:1)

我添加了以下方法并从“TWebModule1::WebModuleBeforeDispatch”事件中调用它。它消除了内存消耗,实际上允许空闲的 REST 服务返回到无会话内存的状态。 DataSnap 肯定需要解决这个问题。

// ---------------------------------------------------------------------------
/// <summary> Memory Restoration. DataSnap opens a session for each call
///         even when the service is set for invocation.
///         Sessions are building up consuming memory and seem not to be freed.
///         See: https://stackoverflow.com/questions/17748300/how-to-release-datasnap-memory-once-connections-are-closed
/// </summary>
/// <remarks> Iterates session in the session manager and closes then terminates
///     any session that has been idle for over 10 seconds.
/// </remarks>
/// <returns> void
/// </returns>
// ---------------------------------------------------------------------------
void TWebModule1::CloseIdleSessions()
{
TDSSessionManager* sessMgr = TDSSessionManager::Instance;
int sessCount = sessMgr->GetSessionCount();
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "Session Count: " + IntToStr(sessCount));
TStringList* sessKeys = new TStringList;
sessMgr->GetOpenSessionKeys(sessKeys);
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "Session Keys Count: " + IntToStr(sessKeys->Count));
TDSSession* sess = NULL;
for(int index = 0; index < sessKeys->Count; index++)
{
    String sessKey = sessKeys->Strings[index];
    sess = sessMgr->Session[sessKey];
    unsigned elapsed = (int)sess->ElapsedSinceLastActvity();
    if(elapsed > 10000)
    {
        WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "CloseSession TerminateSession Key: " + sessKey);
        sessMgr->CloseSession(sessKey);
        sessMgr->TerminateSession(sessKey);
    }
    sess = NULL;
}
delete sessKeys;
sessMgr = NULL;
}