【问题标题】:Reporting memory leaks on shutdown with a console application使用控制台应用程序在关机时报告内存泄漏
【发布时间】:2017-01-27 12:44:42
【问题描述】:

我创建了一个控制台应用程序并设置了 ReportMemoryLeaksOnShutdown := True。

我创建了一个 TStringList 但没有释放它。

程序完成执行后,我看到内存泄漏一秒钟,但随后控制台关闭。

我尝试添加 ReadLn;到最后,但是当我这样做时它只显示一个空白的控制台窗口,这是有道理的。

我需要找到一种在内存泄漏报告之后但在程序完全关闭之前暂停执行的方法。

我正在使用 Delphi 10 Seattle。

program Project1;

{$APPTYPE CONSOLE}

uses
  System.Classes,
  System.SysUtils;

var
  s : TStringList;

begin
  try
    ReportMemoryLeaksOnShutdown := True;
    s := TStringList.Create;

    //ReadLn doesn't work here, which makes sense.
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  //I need to be able to pause the program somewhere after the end statement here.
end.

【问题讨论】:

  • 在之前打开的命令窗口中运行程序,而不是在 IDE 中。
  • 你可以尝试我的AutoConsole单元,见这里:rvelthuis.blogspot.de/2016/07/new-velthuisautoconsole-unit.html。只需将其添加到您的控制台程序即可。
  • 在“system.end”上添加断点。
  • FWIW 我必须按 Ok 来关闭 XE2 中的对话框 - System.ScanForMemoryLeaks 调用 MessageBoxA 阻止执行。
  • @SertacAkyuz 他们真的在后来的一些德尔福中“优化”了它。 :-/

标签: delphi memory-leaks delphi-10-seattle


【解决方案1】:

最简单的方法是在之前打开的命令窗口中运行应用程序。

如果你坚持在IDE中运行时看到内存泄漏报告,请执行以下操作:

  • 在 GetMem.inc 中找到 ShowMessage 过程(Delphi 10 Seattle 的第 4856 行)
  • 在该过程的end; 上放置一个断点。

或者,正如 Sertac Akyuz 评论的那样,在 system 单元的 end. 上放置一个断点。

您还可以将内存泄漏报告重定向到文件。从

下载完整版 FastMM

https://sourceforge.net/projects/fastmm/

或者更好,感谢 Arioch 'The,从这里开始:

https://github.com/pleriche/FastMM4

并在FastMM4Options.inc中设置所需的选项

【讨论】:

  • 这个网址有点老了,新的是github.com/pleriche/FastMM4
  • 他也可以将其重定向到 Windows 调试字符串工具,但我认为这不是他的问题
【解决方案2】:

这是最近的 Delphi 版本中的一个错误。我刚刚在最近的免费 Delphi 10.1 Starter 中检查了它,它的行为与您描述的一样 - 但由于它没有提供 RTL 源,我无法检查确切的原因。

在 Delphi XE2 中,它的行为符合预期:创建任务模式对话框并等待您做出反应,就像 Sertak 所描述的那样。

在Delphi 10.1 中确实会向控制台窗口报告泄漏,但程序并没有停止等待用户注意。这是一个糟糕的解决方案,因为这个原因以及可能在脚本中使用控制台程序(CMD 或 PS 脚本不会“理解”此消息,并且可能会将其与合法输出混淆,并导致后续阶段程序的执行失败。

我认为您必须在 Delphi 10.0 上打开回归类型的错误报告 - 但我认为他们会在 10.2 版本之前修复它。

我还将您的应用程序从 Delphi 分叉的内存管理器切换到原来的应用程序,然后错误行为被恢复:程序显示消息框并等到我将其关闭后才退出到 IDE。

目前我建议您使用提到的原始内存管理器而不是它的 Delphi fork。

program Project1;

{$APPTYPE CONSOLE}

uses
  FastMM4,
  System.Classes,
  System.SysUtils;
...

原始内存管理器位于http://github.com/pleriche/FastMM4 您可以在 Delphi 中使用 Git 客户端,也可以使用独立客户端来保持更新,或者您可以下载一次代码并停止更新,这取决于您。

其代码的相关引用是:

  {$ifdef LogErrorsToFile}
     {Set the message footer}
      LMsgPtr := AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
      {Append the message to the memory errors file}
      AppendEventLog(@LLeakMessage[0], UIntPtr(LMsgPtr) - UIntPtr(@LLeakMessage[1]));
  {$else}
      {Set the message footer}
      AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
  {$endif}
  {$ifdef UseOutputDebugString}
      OutputDebugStringA(LLeakMessage);
  {$endif}
  {$ifndef NoMessageBoxes}
      {Show the message}
      AppendStringToModuleName(LeakMessageTitle, LMessageTitleBuffer);
      ShowMessageBox(LLeakMessage, LMessageTitleBuffer);
  {$endif}
    end;
  end;
{$endif}
end;

{Shows a message box if the program is not showing one already.}
procedure ShowMessageBox(AText, ACaption: PAnsiChar);
begin
  if (not ShowingMessageBox) and (not SuppressMessageBoxes) then
  begin
    ShowingMessageBox := True;
    MessageBoxA(0, AText, ACaption,
      MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY);
    ShowingMessageBox := False;
  end;
end;

此代码依赖于在桌面 Windows 上运行,因此 Embarcadero 可能试图“修复”它以使其跨平台。然而,他们在 Windows 控制台上的做法打破了它....

还可以考虑使用添加其他形式的日志记录 - 到文件和/或 Windows 调试字符串中。它们不会像模态窗口那样引人注目,但至少可以帮助您保存信息,如果您知道在哪里查找信息的话。

【讨论】:

    【解决方案3】:
    var
      SaveExitProcessProc: procedure;
      s: TStringList;
    
    procedure MyExitProcessProc;
    begin
      ExitProcessProc := SaveExitProcessProc;
      readln;
    end;
    
    begin
      SaveExitProcessProc := ExitProcessProc;
      ExitProcessProc := MyExitProcessProc;
      ReportMemoryLeaksOnShutdown := True;
      s := TStringList.Create;
    end.
    

    【讨论】:

    • 错误可能是由于在管理器完成后尝试为 readln 获取内存。在windows上可以用消息框代替。
    • @SertacAkyuz Win32 消息框存在问题:您无法将文本从其中复制到剪贴板。如果确实需要 - 我会重新安装一个存根管理器,将所有 Delphi 调用路由到 Win32 LocalAlloc 和朋友
    • @Arioch - 它代替了 readln,停止执行 - 没有什么可复制的。其次,你错了,在标准 api 消息框上按 ctrl+c,标题、文本和按钮的文本将被复制到剪贴板。
    • @Arioch - 我刚刚测试并确认它可以在 XP 上运行。我相当肯定它也可以在 2K 上运行,但我不再拥有 2K 虚拟机了。
    • @Arioch'实际上,情况正好相反。 Windows 最初具有消息框的复制行为(在 Windows 2000 测试版之一中)。然后在 RAID(早于 QC)中提出了一个建议,为 VCL 添加复制行为,因为测试人员厌倦了添加屏幕截图,然后输入文本以便能够搜索线程。
    【解决方案4】:

    这肯定是一个 hack,不要在生产中使用 :)

    ReportMemoryLeaksOnShutdown:= True;
    IsConsole:= False;
    TStringList.Create;
    

    但是,它会导致泄漏消息(和其他一些消息)显示在消息框中(可以通过按 Ctrl+C 复制所有文本)。

    (使用 Delphi 10.2 测试,请报告我们不喜欢的任何副作用)

    【讨论】:

    • 对于所提出的问题,这是一个完美的解决方案!只需添加IsConsole := false;,就会显示一个包含内存泄漏信息的消息框。
    猜你喜欢
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    相关资源
    最近更新 更多