这是我的队列[i]释放内存的简单代码...... 这是对的吗?
监视器是一个自定义的TIdContext对象。
...
var queue : TList;
...
queue := monitor.Screenshots.LockList;
if queue.Count > 0 then
begin
for i := 0 to queue.Count - 1 do
begin
if TScreenshotInfo(queue[i]).ClientIP = request_AgentIP then
begin
screenshot := TScreenshotInfo(queue[i]);
end;
queue.Delete(i);//Can't free queue[i] from memory?
end;
end;
...
答案 0 :(得分:1)
queue := monitor.Screenshots.LockList;
try
for queue.Count-1 downto 0 do begin
if TScreenshotInfo(queue[i]).ClientIP = request_AgentIP then
TScreenshotInfo(queue[i]).Free;//queue[i] freed
queue.Delete(i);
end;
finally
monitor.Screenshots.UnlockList;
end;
我认为有一个原因可以解释为什么项目被删除,如果它与条件不匹配则不会被释放。
答案 1 :(得分:0)
正如我在your other question on this same issue的评论中所解释的那样:
IdTCPServer_SendExecute()
中有内存泄漏。IdTCPServer_RecvExecute()
将屏幕截图排队到所有已连接的监视器,但IdTCPServer_SendExecute()
仅处理来自特定客户端的屏幕截图,从队列中丢弃所有其他客户端屏幕截图,而不会将其从内存中释放。此外,如果所需客户端的多个屏幕截图位于队列中,则只处理最后一个屏幕截图,丢弃早期的屏幕截图而不释放它们。简而言之,IdTCPServer_SendExecute()
中的循环处理中存在逻辑漏洞。
代码需要看起来更像这样:
destructor TMonitorContext.Destroy;
var
queue: TList;
i: Integer;
begin
queue := Screenshots.LockList;
try
for i := 0 to queue.Count - 1 do begin
TScreenshotInfo(queue[i]).Free;
end;
finally
Screenshots.UnlockList;
end;
Screenshots.Free;
CloseHandle(ScreenshotEvent);
inherited;
end;
procedure TIndyServerForm.IdTCPServer_SendExecute(AContext: TIdContext);
var
monitor: TMonitorContext;
queue: TList;
screenshot: TScreenshotInfo;
request_AgentIP: string;
begin
monitor := TMonitorContext(AContext);
if WaitForSingleObject(monitor.ScreenshotEvent, 1000) <> WAIT_OBJECT_0 then begin
Exit;
end;
// you really should not be requesting an IP on every screenshot sent.
// request an IP once at connection, and don't request a new IP unless
// you want to monitor a different client. This is especially useful
// for allowing IdTCPServer_Recv to not queue screenshots this monitor
// is not interested in receiving...
request_AgentIP := AContext.Connection.IOHandler.ReadLn;
screenshot := nil;
try
queue := monitor.Screenshots.LockList;
try
while queue.Count > 0 do
begin
screenshot := TScreenshotInfo(queue[0]);
queue.Delete(0);
if screenshot.ClientIP = request_AgentIP then
Break;
end;
FreeAndNil(screenshot);
end;
if queue.Count = 0 then
ResetEvent(monitor.ScreenshotEvent);
finally
monitor.Screenshots.UnlockList;
end;
if screenshot = nil then begin
Exit;
end;
// you should send screenshot.ClientIP and screenshot.ClientPort to
// this monitor so it knows which client the screenshot came from...
if not SendStream(AContext, screenshot.Data) then
begin
SOutMsg :='viewer : ' + AContext.Binding.PeerIP + ': reqIP(' + request_AgentIP + ')-> image send failed : ' + KBStr(screenshot.Data.Size) +' @'+TimeToStr(Now);
AContext.Connection.Disconnect;
end
else
begin
SOutMsg :='viewer : ' + AContext.Binding.PeerIP + ': reqIP(' + request_AgentIP + ')-> image send success : ' + KBStr(screenshot.Data.Size)+' @'+TimeToStr(Now);
end;
finally
screenshot.Free;
end;
end;