【问题标题】:Memory leaks problem with using TsoapAttachment in SOAP WebService (Delphi)在 SOAP WebService (Delphi) 中使用 TsoapAttachment 的内存泄漏问题
【发布时间】:2021-12-28 11:14:57
【问题描述】:

我创建了一个简单的 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 动作 webservice 增长 0.2MB(Out.pdf 大小)。当然它会返回所需的文件。

  • GetI_Service 位于从 WSDL 自动生成的文件中
  • 网站最终是将上传的文件转换为TSoapAttachment中的TSoapAttachment返回。

【问题讨论】:

  • 您的代码无法编译。 T_Service.Convert 不完整,因此可能存在其他问题,但您有泄漏,因为您从不释放 outStream 并且您应该使用 RetAttach.SetSourceStream(outStream, soOwned); 来处理不再需要时释放该流。
  • 同样用raisetry...finally 包裹在try..except 中是没有意义的。 try...finally 不用于处理异常,它将传播到下一个异常处理程序。 try..finally 只在 finally 块中运行代码,不管异常
  • @DalijaPrasnikar 这就是重点。当我发布 outStream:finally outStream.Free end; 然后我在客户端中没有返回的数据,并且我有一个异常“访问冲突在...”。对于 TSoapAttachment 客户端应该释放内存。在 Soap.InvokeRegistry.pas 理论上它的集合:'RemClassRegistry.RegisterXSClass(TSOAPAttachment, ... 实际上它不起作用。 try...try.. 为简单起见,我删除了错误处理。
  • 是的,你不能手动释放outStream,这就是为什么我说你需要使用soOwned 而不是soReference所以outStream内存将由RetAttach处理跨度>

标签: delphi


【解决方案1】:

您正在泄漏 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;

【讨论】:

  • RetAttach.SetSourceStream(outStream, soOwned); 这是解决问题的方法。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-28
相关资源
最近更新 更多