在 SOAP WebService 中使用 TsoapAttachment 的内存泄漏问题 (Delphi)

Memory leaks problem with using TsoapAttachment in SOAP WebService (Delphi)

我创建了一个简单的 webservie,但我遇到了应用程序内存增长的问题。

网络服务:

// Impl File
type

  { T_Service }
  T_Service = class(TInvokableClass, I_Service)
  public
    function Convert(ReqAttach: TSOAPAttachment): TSOAPAttachment; stdcall;
  end;
  
implementation

function T_Service.Convert(
  ReqAttach: TSOAPAttachment): TSOAPAttachment;
var
  memStream : TMemoryStream;
  outStream : TMemoryStream;  
 try
    ReqAttach.SaveToStream(memStream);
    outStream.LoadFromFile('D:\Out.pdf');
    RetAttach.SetSourceStream(outStream, soReference);
  finally
    memStream.Free;
  end;
end;

initialization
{ Invokable classes must be registered }
   InvRegistry.RegisterInvokableClass(T_Service);
end.
//Intf file
type

  I_Service = interface(IInvokable)
    ['{69440128-AC9D-43CC-9A11-7B6B36F15D1E}']
  function Convert(ReqAttach: TSOAPAttachment): TSOAPAttachment; stdcall;
end;
implementation

initialization
  { Invokable interfaces must be registered }
  InvRegistry.RegisterInterface(TypeInfo(I_Service));

end.

我的客户

procedure TClientForm.btnClick(Sender: TObject);
var
  ReqAttach: TSOAPAttachment;
  RespAttach: TSOAPAttachment;
  Service: I_Service;
begin
  ReqAttach := TSOAPAttachment.Create;
  Service := unitIMyService.GetI_Service();
  try
    try
      ReqAttach.SetSourceFile('D:\In.pdf');
      RespAttach := Service.Convert(ReqAttach);
      RespAttach.SaveToFile('D:\Resp.pdf');
    finally
      FreeAndNil(ReqAttach);
      FreeAndNil(RespAttach);
    end;
  except
    raise;
  end;
end;

每次执行 btnClick 动作时,web 服务增长 0.2MB(Out.pdf 大小)。当然,它 return 返回所需的文件。

您正在泄漏 outStream 因为您将其作为参考传递给 RetAttach

RetAttach.SetSourceStream(outStream, soReference);

您需要使用 soOwned 作为 Ownership 参数。在这种情况下,outStream 的内存管理将转移到 RetAttach 实例,并将自动处理。

RetAttach.SetSourceStream(outStream, soOwned);

您还不必要地用 try...except 包装了 try...finally,除了重新引发异常之外什么都不做。

  try
    try
      ...
    finally
      FreeAndNil(ReqAttach);
      FreeAndNil(RespAttach);
    end;
  except
    raise;
  end;

你应该删除那个无用的 try...except 块,因为下面的代码具有完全相同的效果。

try
  ...
finally
  FreeAndNil(ReqAttach);
  FreeAndNil(RespAttach);
end;

try...finally 不处理异常它只是在 finally 块中运行代码而不考虑异常。该代码运行后,异常将自动传播到下一个异常处理程序。


代码中的其他问题是 RespAttach 未初始化为 nil,如果异常发生在分配之前,您的调用 FreeAndNil(RespAttach) 可能是悬空指针上的 运行。除此之外 FreeAndNil 在这里也是多余的,因为您正在处理不会被重用的局部变量。

procedure TClientForm.btnClick(Sender: TObject);
var
  ReqAttach: TSOAPAttachment;
  RespAttach: TSOAPAttachment;
  Service: I_Service;
begin
  RespAttach := nil; 
  ReqAttach := TSOAPAttachment.Create;
  try
    Service := unitIMyService.GetI_Service();
    ReqAttach.SetSourceFile('D:\In.pdf');
    RespAttach := Service.Convert(ReqAttach);
    RespAttach.SaveToFile('D:\Resp.pdf');
  finally
    ReqAttach.Free;
    RespAttach.Free;
  end;
end;