Delphi TWebBrowser 内存泄漏问题

4

我的应用程序使用一个TWebBrowser控件来加载网页。问题是,在关闭包含TWebBrowser的窗体后,所使用的内存并没有被释放。如果我打开和关闭窗体,内存使用量只会持续增加。

有一些帖子提到调用SetProcessWorkingSetSize()或CoFreeUnusedLibrariesEx()来解决这个问题,但我不确定这些是否是正确的解决方案。

有任何想法如何释放TWebBrowser使用的内存吗?


6
我猜想您可能误解了用于收集这些统计信息的工具。堆分配器非常复杂,它们通常会保留内存直到需要释放为止,如果系统有足够的可用内存,为什么要花时间将其返回给操作系统呢?与其这样,不如免费地保留它。 - David Heffernan
1
你是如何关闭(和打开)窗体的?是否有任何释放操作?请提供源代码。 - mj2008
如果您打开并关闭表单而不加载网页,您是否仍然有内存问题? - Sam M
3
这是一个泄漏应用程序的例子,您可以在这里找到。即使使用TWebBrowser作为组件的表单被释放,线程仍然保持运行,并且每个新实例都会创建新的线程(因此内存消耗增加)。我没有找到解决方案,我尝试了许多方法,包括CoFreeUnusedLibrariesEx,导航到about:blank,但都没有帮助。这是一个严重的问题,因为创建几个表单实例会耗费许多系统资源。我的建议是隐藏表单,而不是释放它,因为似乎这里没有效果。 - TLama
多年过去了,至今仍未找到解决方案 : / - delphirules
我花了很多时间和精力研究,得出结论:有些网站编码不良,即使释放TWebBrowser组件,也无法释放内存。我的解决方案很简单,就是在接近内存限制时关闭应用程序并重新启动它。关闭/重新启动是自动化的。 - Kevin Davidson
6个回答

5

QC#106829描述了TWebBrowser内存泄漏的一种可能原因。访问Document(以及通过TOleControl.GetIDispatchPropTOleControl.GetIUnknownProp实现的任何其他属性)会导致泄漏,因为它调用了AddRef而没有调用Release。解决方法是手动调用Release,或者修补VCL(参见此处),或者避免使用有问题的属性(例如,使用browser.DefaultInterface.Document代替browser.Document)。


使用 browser.DefaultInterface.Document 替代 browser.Document 解决了我的内存泄漏问题。谢谢。 - Charles-Henri
请注意,Delphi 10.0 Seattle中已修复了此TOleControl错误。 - Remy Lebeau

1

使用TWebBrowser会在幕后执行许多工作,其中大部分与完整的Internet Explorer实例执行的工作相同。它对您隐藏,但仍然存在,很可能我们无法强制从内存中删除它。在页面加载之前和之间检查内存使用情况,并测试调用Navigate('about:blank');时会发生什么。还要检查您的析构函数是否被正确调用,并考虑从OnClose或OnCloseQuery中调用Navigate('about:blank');。我发现这确实有助于内存情况稍微好一点。


0

最好的解决方案是停止使用TWebbrowser。

CEF4Delphi是一个免费的库,它使用Chrome而不是Internet Explorer。始终保持最新和非常高效:

https://github.com/salvadordf/CEF4Delphi


-1
Uses Winapi.PsAPI;
 ...
{$IFDEF WIN32}
procedure TForm1.MemoryFree;
var
  HandleCaptureProcessus: THandle;
  UnProcessus: TProcessEntry32;
  PIDProcessus: THandle;
  HandleProcessus: THandle;
  NameOfProcess: string;
begin
  PIDProcessus := 4294967295;
  NameOfProcess := ExtractFileName(Application.ExeName);

  HandleCaptureProcessus := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  UnProcessus.dwSize := SizeOf(TProcessEntry32);

  Process32First(HandleCaptureProcessus, UnProcessus);
  repeat
    if UnProcessus.szExeFile = NameOfProcess then
    begin
      PIDProcessus := UnProcessus.th32ProcessID;
      Break;
    end;
  until not Process32Next(HandleCaptureProcessus, UnProcessus);

  if PIDProcessus = 4294967295 then
  begin
    CloseHandle(HandleCaptureProcessus);
    exit;
  end;

  HandleProcessus := OpenProcess(PROCESS_ALL_ACCESS, False, PIDProcessus);

  EmptyWorkingSet(HandleProcessus);

  CloseHandle(HandleProcessus);
end;
{$ELSE}
procedure TForm1.MemoryFree;
begin
  //**
end;
{$ENDIF}

为了清除内存,我使用了在论坛上找到的这个函数。它比SetProcessWorkingSetSize()方法更好地清除了“工作集”,但是调用起来更加困难,并且需要在Winapi.PsAPI单元中注册。 但是,我注意到这两个函数都可以清除“工作集”。如果您查看任务管理器中的“分配的内存”列,您会发现该参数并没有被清除。我的应用程序在清理后的“工作集”可以减少到10 MB,但所有分配的内存仍将保持为1.5 GB。而且,我认为这就是导致“内存不足”错误的原因。如果您长时间浏览重负载网站,这个错误仍然会出现。

-2
procedure TForm1.FreeMemory;
begin
    if Win32Platform = VER_PLATFORM_WIN32_NT then
    SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);
end;

并且定时调用它

FreeMemory;

这个解决方案不起作用;它只是“压缩”了所使用的内存,但在新的导航后,之前使用的所有大内存都会再次分配。 - delphirules

-2

为释放内存,只需初始化新文档:

(WebBrowser.Document as IPersistStreamInit).InitNew;

这个解决方案不起作用,内存仍然在增加。 - delphirules

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接