Are these Memory Leaks Real

Joh*_*rat 0 delphi

I have been running FastMM4 on my code to see if I have any memory leaks...

This has reported a load of class UniCodeString leaks when I close the program. Are these real and how do I interpret the Event Log: This is a typical block report:

--------------------------------2020/10/21 17:32:01--------------------------------
A memory block has been leaked. The size is: 120

This block was allocated by thread 0x5648, and the stack trace (return addresses) at the time was:
430547 [FastMM4.pas][FastMM4][_ZN7Fastmm411DebugGetMemEx][8737]
409534 [System.pas][System][_ZN6System7_GetMemEx][4803]
412F8C [System.pas][System][_ZN6System17_NewUnicodeStringEi][25403]
414C3C [System.pas][System][_ZN6System16InternalUStrCatNERNS_13UnicodeStringEiPS0_][29902]
4156CB [System.pas][System][_ZN6System9_UStrCatNERNS_13UnicodeStringEi][30998]
5DD71A [System.IniFiles.pas][System.IniFiles][_ZN6System8Inifiles11TMemIniFile8TSection9SetValuesEiNS_13UnicodeStringE][852]
5DEFA6 [System.IniFiles.pas][System.IniFiles][_ZN6System8Inifiles11TMemIniFile11WriteStringENS_13UnicodeStringES2_S2_][1212]
14F335F [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings13UpdateSectionEN6System13UnicodeStringE][1153]
14F26E7 [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings13UpdateSectionEi][1087]
14F24FD [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings4SaveEiiiiibjPN6System7Classes8TStringsENS1_13UnicodeStringE][1078]
14DE604 [IDEMain.pas][IDEMain][_ZN7Idemain10TfmIDEMain9FormCloseEPN6System7TObjectERNS1_7Uitypes12TCloseActionE][573]

The block is currently used for an object of class: UnicodeString

The allocation number is: 675683

Current memory dump of 256 bytes starting at pointer address 7FF4FBF7E170:
24 D9 69 01 B0 04 02 00 01 00 00 00 29 00 00 00 50 00 61 00 74 00 68 00 3D 00 43 00 3A 00 5C 00
50 00 72 00 6F 00 67 00 72 00 61 00 6D 00 20 00 46 00 69 00 6C 00 65 00 73 00 20 00 28 00 78 00
38 00 36 00 29 00 5C 00 50 00 72 00 6F 00 74 00 6F 00 6E 00 49 00 44 00 45 00 5C 00 50 00 44 00
53 00 00 00 CA 9A 8A F5 AF D5 F2 FF 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
00 00 00 00 00 00 00 00 81 E3 F7 FB F4 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 37 48 0A 00 47 05 43 00 00 00 00 00 B6 08 43 00 00 00 00 00
9D 95 40 00 00 00 00 00 1C 54 41 00 00 00 00 00 11 55 41 00 00 00 00 00 01 56 45 01 00 00 00 00
63 02 46 01 00 00 00 00 E2 1E 46 01 00 00 00 00 92 9B 68 00 00 00 00 00 A5 04 41 00 00 00 00 00
$  Ù  i  .  °  .  .  .  .  .  .  .  )  .  .  .  P  .  a  .  t  .  h  .  =  .  C  .  :  .  \  .
P  .  r  .  o  .  g  .  r  .  a  .  m  .     .  F  .  i  .  l  .  e  .  s  .     .  (  .  x  .
8  .  6  .  )  .  \  .  P  .  r  .  o  .  t  .  o  .  n  .  I  .  D  .  E  .  \  .  P  .  D  .
S  .  .  .  Ê  š  Š  õ  ¯  Õ  ò  ÿ  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
.  .  .  .  .  .  .  .    ã  ÷  û  ô    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
.  .  .  .  .  .  .  .  .  .  .  .  7  H  .  .  G  .  C  .  .  .  .  .  ¶  .  C  .  .  .  .  .
  •  @  .  .  .  .  .  .  T  A  .  .  .  .  .  .  U  A  .  .  .  .  .  .  V  E  .  .  .  .  .
c  .  F  .  .  .  .  .  â  .  F  .  .  .  .  .  ’  ›  h  .  .  .  .  .  ¥  .  A  .  .  .  .  .
Run Code Online (Sandbox Code Playgroud)

Most of the leaks are small, less than 200 bytes but should I be worrying about them?

Rem*_*eau 15

这些是真的吗

非常可能。特别是你展示的那个,是的。

我如何解释事件日志

您显示的泄漏报告表明 aUnicodeString已泄漏。 UnicodeString是托管类型。我见过UnicodeString泄露的唯一时间是:

  • 它是classor的成员record,并且该类型的动态分配的实例本身已泄漏。泄漏报告应显示此实例也被泄漏。

  • UnicodeString由于意外覆盖,指向已分配的指针已损坏,通常是由代码中某处的逻辑错误造成的。

  • 它被声明为 athreadvar并且在线程退出之前不会被手动清除。这是记录在案的行为

    通常由编译器管理的动态变量(长字符串、宽字符串、动态数组、变体和接口)可以用 声明 threadvar,但编译器不会自动释放每个执行线程创建的堆分配内存。如果您在线程变量中使用这些数据类型,则您有责任在线程终止之前从线程内部处理它们的内存。

让我们更仔细地查看您显示的报告。

    内存块已泄漏。尺码为:120

不言自明。分配大小为 120 字节的内存块被泄露。

    该块由线程 0x5648 分配,当时的堆栈跟踪(返回地址)为:
    430547 [FastMM4.pas][FastMM4][_ZN7Fastmm411DebugGetMemEx][8737]
    409534 [System.pas][系统][_ZN6System7_GetMemEx][4803]
    412F8C [System.pas][System][_ZN6System17_NewUnicodeStringEi][25403]
    414C3C [System.pas][System][_ZN6System16InternalUSrCatNERNS_13UnicodeStringEiPS0_][29902]
    4156CB [System.pas][系统][_ZN6System9_USrCatNERNS_13UnicodeStringEi][30998]
    5DD71A [System.IniFiles.pas][System.IniFiles][_ZN6System8Inifiles11TMemIniFile8TSection9SetValuesEiNS_13UnicodeStringE][852]
    5DEFA6 [System.IniFiles.pas][System.IniFiles][_ZN6System8Inifiles11TMemIniFile11WriteStringENS_13UnicodeStringES2_S2_][1212]
    14F335F [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings13UpdateSectionEN6System13UnicodeStringE][1153]
    14F26E7 [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings13UpdateSectionEi][1087]
    14F24FD [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings4SaveEiiiiiibjPN6System7Classes8TStringsENS1_13UnicodeStringE][1078]
    14DE604 [IDEMain.pas][IDEMain][_ZN7Idemain10TfmIDEMain9FormCloseEPN6System7TObjectERNS1_7Utypepes12TCloseActionE][573]

泄漏的内存由 ID 为0x5648运行时的线程分配。堆栈跟踪显示了导致泄漏内存块分配的函数调用链。在这种情况下,堆栈跟踪从您的TForm.OnClose事件处理程序开始,因此该线程显然是主 UI 线程。实际的函数调用链是:

  • IDEMain.TfmIDEMain.FormClose()in IDEMain.pas,其中调用:
  • IDEIni.TIDEIniSettings.Save()in IDEIni.pas,其中调用:
  • IDEIni.TIDEIniSettings.UpdateSection(Integer)in IDEIni.pas,其中调用:
  • IDEIni.TIDEIniSettings.UpdateSection(UnicodeString)in IDEIni.pas,其中调用:
  • System.IniFiles.TMemIniFile.WriteString()in System.IniFiles.pas,其中调用:
  • System.IniFiles.TMemIniFile.TSection.SetValues()in System.IniFiles.pas,其中调用:
  • System._UStrCatN()in System.pas,其中调用:
  • System.InternalUStrCatN()in System.pas,其中调用:
  • System._NewUnicodeString()in System.pas,其中调用:
  • System._GetMem()in System.pas,其中调用:
  • FastMM4.DebugGetMem()in FastMM4.pas,它分配了泄漏的内存

因此,您的OnClose处理程序正在将一个字符串值保存到一个.INI文件中,并且在内部该 write 在 内部执行了一个字符串连接TSection.SetValues(),其结果被泄露。我猜是因为连接的字符串保存在一个TSection本身泄漏的对象中。

    该块当前用于类的对象:UnicodeString

不言自明。

    分配号为:675683

FastMM 会跟踪它在程序生命周期内执行了多少内存分配。

    从指针地址 7FF4FBF7E170 开始的 256 字节的当前内存转储:
    24 D9 69 01 B0 04 02 00 01 00 00 00 29 00 00 00 50 00 61 00 74 00 68 00 3D 00 43 00 3A 00 5C 00
    50 00 72 00 6F 00 67 00 72 00 61 00 6D 00 20 00 46 00 69 00 6C 00 65 00 73 00 20 00 28 00 78 00
    38 00 36 00 29 00 5C 00 50 00 72 00 6F 00 74 00 6F 00 6E 00 49 00 44 00 45 00 5C 00 50 00 44 00
    53 00 00 00 CA 9A 8A F5 AF D5 F2 FF 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
    00 00 00 00 00 00 00 00 81 E3 F7 FB F4 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 37 48 0A 00 47 05 43 00 00 00 00 00 B6 08 43 00 00 00 00 00
    9D 95 40 00 00 00 00 00 1C 54 41 00 00 00 00 00 11 55 41 00 00 00 00 00 01 56 45 01 00 00 00 00
    63 02 46 01 00 00 00 00 E2 1E 46 01 00 00 00 00 92 9B 68 00 00 00 00 00 A5 04 41 00 00 00 00 00
    $Ù我。°。. . . . . . )。. . 。一种 。吨。H 。= . C 。:。\ .
    。r 。哦。G 。r 。一种 。米。. F 。一世 。升。电子。。. ( 。 X 。
    8 . 6 . )。\ . 。r 。哦。吨。哦。名词。一世 。D. 乙。\ . 。D.
    。. . Ê š õ ¯ Õò ÿ € € € € € € € € € € € € € € € € €
    . . . . . . . . ã÷ûô。. . . . . . . . . . . . . . . . .
    . . . . . . . . . . . . 7 小时。. G 。C 。. . . . ¶ C 。. . . .
      • @。. . . . . TA。. . . . . UA 。. . . . . 维。. . . .
    C 。F 。. . . . 一种 。F 。. . . . ' > H 。. . . . ¥ . 一种 。. . . .

这是泄漏的内存块中原始数据的转储。该块位于内存地址$7FF4FBF7E170。前半部分是原始字节的十六进制格式表示,后半部分是这些字节的人类可读的 ASCII 解释。

由于我们知道该块属于 a UnicodeString,我们可以进一步挖掘数据。

AUnicodeStringStrRec标题开头:

    A memory block has been leaked. The size is: 120

日志中使用的名称 mangling 的格式告诉我您使用的是基于 Clang 的编译器之一,因此如果我们假设是 64 位编译器,那么StrRec大小将是 16 个字节,如果我们分解前 16 个字节在转储中,我们得到以下值:

24 D9 69 01 _Padding,忽略
B0 04 代码页 = 1200,UTF-16
02 00 elemSize = 2, sizeof(WideChar)
01 00 00 00 refCnt = 1
29 00 00 00 长度 = 41 WideChar 元素

这与有效的UnicodeString. 因此,如果我们查看((41+1)*2)=84转储中的下一个字节,我们会看到以下内容:

50 00 61 00 74 00 68 00 3D 00 43 00 3A 00 5C 00
50 00 72 00 6F 00 67 00 72 00 61 00 6D 00 20 00
46 00 69 00 6C 00 65 00 73 00 20 00 28 00 78 00
38 00 36 00 29 00 5C 00 50 00 72 00 6F 00 74 00
6F 00 6E 00 49 00 44 00 45 00 5C 00 50 00 44 00
53 00 00 00

并从相应的 ASCII 转储:

。一种 。吨。H 。= . C 。:。\ .
。r 。哦。G 。r 。一种 。米。.
F 。一世 。升。电子。。. ( 。 X 。
8 . 6 . )。\ . 。r 。哦。吨。
哦。名词。一世 。D. 乙。\ . 。D.
。. .

其中,考虑到 UTF-16,形成了 Unicode 字符串值:

'Path=C:\Program Files (x86)\ProtonIDE\PDS'

那是泄漏的实际字符串。在结合动作TSection.SetValues()很可能加入子'Path''=''C:\Program Files (x86)\ProtonIDE\PDS'一起,假设TMemIniFile.WriteString()有被称为是这样的:

    This block was allocated by thread 0x5648, and the stack trace (return addresses) at the time was:
    430547 [FastMM4.pas][FastMM4][_ZN7Fastmm411DebugGetMemEx][8737]
    409534 [System.pas][System][_ZN6System7_GetMemEx][4803]
    412F8C [System.pas][System][_ZN6System17_NewUnicodeStringEi][25403]
    414C3C [System.pas][System][_ZN6System16InternalUStrCatNERNS_13UnicodeStringEiPS0_][29902]
    4156CB [System.pas][System][_ZN6System9_UStrCatNERNS_13UnicodeStringEi][30998]
    5DD71A [System.IniFiles.pas][System.IniFiles][_ZN6System8Inifiles11TMemIniFile8TSection9SetValuesEiNS_13UnicodeStringE][852]
    5DEFA6 [System.IniFiles.pas][System.IniFiles][_ZN6System8Inifiles11TMemIniFile11WriteStringENS_13UnicodeStringES2_S2_][1212]
    14F335F [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings13UpdateSectionEN6System13UnicodeStringE][1153]
    14F26E7 [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings13UpdateSectionEi][1087]
    14F24FD [IDEIni.pas][IDEIni][_ZN6Ideini15TIDEIniSettings4SaveEiiiiibjPN6System7Classes8TStringsENS1_13UnicodeStringE][1078]
    14DE604 [IDEMain.pas][IDEMain][_ZN7Idemain10TfmIDEMain9FormCloseEPN6System7TObjectERNS1_7Uitypes12TCloseActionE][573]

剩下的转储数据只是碰巧在同一个内存块中的随机垃圾,因为 FastMM 在固定大小的块中分配内存。在这种情况下,泄漏的数据UnicodeString被分配在一个使用 120 字节块的桶中。

如果我不得不猜测,要么你泄露了TMemIniFile对象(它应该出现在泄漏报告的其他地方),或者TMemIniFile你的 Delphi 版本有一个逻辑错误,泄漏了一个TSection对象(它应该出现在泄漏报告的其他地方)。您现在可以在此处开始调试代码以跟踪泄漏的根本原因。

  • 我非常感谢你的解释。我相信许多其他人也会发现这确实很有帮助。 (3认同)
  • UnicodeString 容易泄漏的另一种方式是如果将其声明为 threadvar。 (2认同)