Indy:Object [i] .delete方法可以释放内存吗?

时间:2015-11-25 08:47:00

标签: delphi indy10

这是我的队列[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;
...

2 个答案:

答案 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;