连接的 Delphi 字符串是否保存在保留对该字符串的引用的隐藏临时变量中?

2024-03-23

我试图了解 Delphi 服务器应用程序中的内存问题:最初我怀疑存在彻底的泄漏,但现在相信我们看到内存挂起的时间比应有的时间长,因为编译器在用 + 动态连接字符串时使用了隐藏的临时值。 ,导致痛苦的自由空间内存碎片。

背景:

这是 Windows 上的一套 32 位服务器应用程序,Delphi 版本相当旧,我认为它是 7,但肯定是 Unicode 之前的,并使用 Nexus 3 内存管理器,我在其中编写了一个 DLL 来挂钩所有分配/free 调用(以及千兆字节的内存跟踪)。

我有应用程序源代码,但没有编译器;我不是这个应用程序的开发人员(甚至不是 Delphi 开发人员),但创建了广泛的自定义工具来监视、跟踪和分析内存。我一直在 IDA Pro 反汇编程序中将 .EXE 拆开。

一些示例代码:

我试图将其减少到最低限度;这段代码不打算编译:

procedure TaskThread.RunWorkLoop
begin
    while not Terminated do
    begin

      tsk := WaitForWorkToDo();  // this could sit for minutes at a time

      SetThreadName('Working on ' + tsk.Name);

      tsk.Run(); // THIS COULD TAKE A LONG TIME

      SetThreadName('Idle');
   end
end;

SetThreadName()接受一个常量字符串参数并挂起它,以便系统的其他部分知道该线程正在做什么。

我对代码的反汇编表明,编译器已分配了一个隐藏的局部临时变量来接收“正在处理”和任务名称部分的串联,这就是传递给的内容SetThreadName,其中还保留了字符串的句柄。

当任务正在运行时 - 这可能是 20 分钟 - 我相信有two字符串的句柄。其中一个被保存在SetThreadName,另一个是暂时隐藏的。

这一切都很好。

然后,当任务结束并且线程名称设置为'Idle', SetThreadName()释放原始字符串并分配文字Idle.

但是:我相信隐藏的本地临时仍然保留该字符串的句柄,并且 refcount=1,因此它将占用空间,直到过程返回,或者next循环会覆盖隐藏的本地临时值,释放旧值。

在此期间,程序无法访问它,无法显式释放它,并且没有任何有用的目的,但仍在消耗内存。

对于大多数过程来说,这并不重要,因为它们的开始和结束彼此相对接近,因此所有内容都会立即释放,但在循环服务器应用程序中,这些可以保留更长时间。这导致我们的内存碎片。

情况变得更糟

在实际应用中,更多的是这样的:

SetThreadName(tsk.Name + '-' + FormatDateTime('mm/dd/yy hh:nn:ss', Now));

在这种情况下,有two隐藏的临时变量:一个用于结果FormatDateTime,另一个用于整体串联结果,实际上运行为:

tmp1: String;
tmp2: String;
...
  tmp1 := FormatDateTime('...');
  tmp2 := tsk.Name + '-' + tmp1;
  SetThreadName(tmp2);

我确信我看到了字符串结果FormatDateTime任务完成后很长一段时间都在记忆中徘徊,我已经看到了字面上地是位于 1 MB 内存部分中间的单个 ~30 字节分配,周围有可用空间; Nexus3MM用途VirtualAlloc分配更大的操作系统级块。

那个 30 字节的字符串will最终会在下一个循环或程序退出时被释放,所以我确定它不是leak,但我希望当我们完成它时,位于孤独的 1 MB 部分中间的单个 30 字节分配实际上会消失,以便整个部分可以释放到操作系统。

但如果它停留的时间足够长,内存管理器就会从中分配其他东西,并且内存中的这个漏洞会变得更加永久。

我们有非常详细的繁忙/空闲内存映射,并且确信这种碎片正在杀死我们(这当然不是唯一的原因)。

我的问题:

1)我的理解正确吗?

2)如果是这样,这是通过使用显式临时变量来消除隐藏临时变量的唯一解决方法,我们可以这样做:

tmp1: String;
tmp2: String;
...
  tmp1 := FormatDateTime('...');
  tmp2 := tsk.Name + '-' + tmp1;
  SetThreadName(tmp2);
  tmp1 := '';  // release the date/time string
  tmp2 := '';  // release the overall thread name string

我非常有信心我必须这样做FormatDateTime中间结果(我已经具体看到过),但不确定整体串联。

这只是感觉不对。

编辑:几周后只是更新。我们重写了中央循环以使用显式临时变量,这实际上对某些关键服务器进程的内存碎片产生了明显的(尽管不是主要的)差异。我们还有其他事情需要研究,但我很清楚这是一条值得走的路。


根据我的经验,它的工作原理确实如此。我不确定这是通过合同还是通过实施。我想随着最近添加的内联变量声明,现在可能会略有不同。但在 pre-unicode Delphi 中,我相信它的工作原理与您所描述的完全一样。

所有使用托管类型变量(隐式或显式)的例程,或包含该变量的记录,都将生成隐式try/finally例程中的块,与finally部分清除参考。你的代码真正做的是:

procedure TaskThread.RunWorkLoop
var
  sImplicit : string;
begin
  sImplicit := '';
  try
    while not Terminated do
    begin
      tsk := WaitForWorkToDo();  // this could sit for minutes at a time

      sImplicit := 'Working on ' + tsk.Name;

      SetThreadName(sImplicit);

      tsk.Run(); // THIS COULD TAKE A LONG TIME

      SetThreadName('Idle');
    end;
  finally
    sImplicit := '';
  end;
end;

在您的情况下,由于您从未退出使用隐式变量的例程,因此它确实保留在内存中。

至于解决方案,我相信你提出的方案会起作用。但您也可以简单地将代码移动到另一个方法(或本地过程)。

procedure TaskThread.RunWorkLoop
  procedure JustKeepWorking;
  begin
    tsk := WaitForWorkToDo();  // this could sit for minutes at a time
    SetThreadName('Working on ' + tsk.Name);
    tsk.Run(); // THIS COULD TAKE A LONG TIME
    SetThreadName('Idle');
  end;
begin
  while not Terminated do
  begin
    JustKeepWorking;
  end
end;

另外,您可能想检查这个问题 https://stackoverflow.com/questions/3250827/initialise-string-function-result/3251381以获得更多见解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

连接的 Delphi 字符串是否保存在保留对该字符串的引用的隐藏临时变量中? 的相关文章

  • PHP 中的多行字符串文字

    考虑 xml l xml vv echo xml 这将回响vv 为什么以及如何为诸如此类的事情执行多行字符串文字简单XML https en wikipedia org wiki SimpleXML etc Well xml l vv Wo
  • 将 scanf 与 NSString 一起使用

    我希望用户输入一个字符串 然后将输入分配给 NSString 现在我的代码如下所示 NSString word scanf s word The scanf http www cplusplus com reference clibrary
  • 在 Delphi 2009 上安装最新版本的 Indy 10 [重复]

    这个问题在这里已经有答案了 是否有更新 Delphi 2009 中的 Indy 10 组件的分步指南 我读过正在卸载线程 https stackoverflow com questions 486210 what is the proper
  • 如何向标准集合编辑器添加图标?

    我有一个自定义控件 它利用TCollection and TCollectionItem 在集合编辑器中 我想向每个列表项添加图标 该列表项由内部TImageList 在其父组件内 集合项本身代表图标 我想在这个编辑器中显示相应的图标 如何
  • strlen - 字符串的长度有时会增加 1

    我正在做一些 C 智力题 在大多数情况下 我能够找到正确的答案 但我遇到了问题 我通过使用编译器知道正确的答案 但我不知道原因 看一下代码 char c abc 012 0x34 什么会strlen c 返回 使用标准 C 编译器 我的编译
  • 在TImageViewer中,如何获取用户点击图片的位置?

    在TImageViewer控件中 用户可以缩放或平移图片 我的问题是 当用户点击图片时 如何获取用户在图片上的点击位置 尤其是用户可以对图片进行放大 缩小或平移之后 如何获取对应的图片点击位置呢 As shown below How to
  • Java中如何对对象数组进行排序?

    我的数组不包含任何字符串 但它包含对象引用 每个对象引用都通过 toString 方法返回名称 id 作者和发布者 public String toString return name n id n author n publisher n
  • 这个看不见的空间是如何创造出来的?

    FileTitle FileTitle false 第一个字符串和最后一个字符串之间有一个空格e and FileTitle length 12 FileTitle length 11 这两个字符之间存在代码为 8203 的 Unicode
  • 将 Python 输入字符串限制为特定字符和长度

    我刚刚开始学习我的第一种真正的编程语言 Python 我想知道如何限制用户输入raw input特定字符和特定长度 例如 如果用户输入包含除字母之外的任何内容的字符串 我想显示一条错误消息a z 我想显示超过 15 个字符的用户输入之一 第
  • 检查子字符串是否在字符串列表中?

    我之前已经找到了这个问题的一些答案 但它们对于当前的Python版本来说似乎已经过时了 或者至少它们对我不起作用 我想检查字符串列表中是否包含子字符串 我只需要布尔结果 我找到了这个解决方案 word to check or wordlis
  • C# 中单个 & 符号的第二个含义是什么?

    我在 C 中使用了单个与号 来表示 检查second条件语句即使第一个是false 但以下似乎是不同的意思 of 总而言之 谁能解释一下如何i 1在下面的例子中有效吗 List
  • 如何在 C++ 中将 CString 转换为 double?

    我如何转换CString to a double在 C 中 Unicode 支持也很好 Thanks A CString可以转换为LPCTSTR 这基本上是一个const char const wchar t 在 Unicode 版本中 知
  • Delphi 5 的哈希表实现 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 您知道 Delphi 5 的良好且免费的哈希表实现吗 我需要在哈希表中组织大量数据 并且我有点担心在网
  • 查找Delphi项目中的所有编译错误

    我正在对我的 Delphi 项目进行一些重构 我希望能够做出改变 然后看看all项目中因该更改而中断的地方 类似于 Eclipse 列出项目的所有编译错误 在 Java 中 在 Delphi 中 我可以进行更改 然后重新编译我的项目 但编译
  • 如何将 R 数据框中的多个字符列合并为单个列

    我正在处理人口普查数据 需要将四个字符列合并为一列 Example LOGRECNO STATE COUNTY TRACT BLOCK 60 01 001 021100 1053 61 01 001 021100 1054 62 01 00
  • 将字符串转换为双精度 - VB

    VB中有没有一种有效的方法来检查字符串是否可以转换为双精度型 我目前正在尝试将字符串转换为双精度型 然后查看它是否引发异常 但这似乎减慢了我的申请速度 Try if number then format it current CDbl x
  • Swift:检查 UISearchBar.text 是否包含 url

    如何检查 UISearchBar text 是否包含 URL 我想做这样的事情 if searchBar text NSTextCheckingType Link 但我收到错误 String is not convertible to NS
  • Delphi 2010 - 从 XML 文档解码 Base64 编码图像

    我正在尝试从应用程序中的 XML 文档解码 base64 编码的 EMF 图像并将其呈现在屏幕上 但是 它似乎从未出现 如果我将 XML 文档中的数据复制 粘贴到 Notepad 中并使用Base64 Decode选项并将文件另存为 emf
  • 生成逗号分隔值

    假设我有一个字符串集合 foo bar xyz 我想从列表中生成一个逗号分隔的值 如下所示 foo bar xyz 请注意末尾缺少 我知道有多种方法可以生成此内容 使用 for 循环和 string Format 或 StringBuild
  • Python 中的 Unix cat 函数 (cat * > merged.txt)? [复制]

    这个问题在这里已经有答案了 一旦建立了目录 有没有办法在Python中使用Unix中的cat函数或类似的函数 我想将 files 1 3 合并到 merged txt 我通常会在 Unix 中找到该目录 然后运行 cat gt merged

随机推荐

  • 如何将index.html、jquery.js、main.css捆绑在DLL中以在WPF中使用?

    我知道有可能捆绑 eg index html main css jQuery js Logo gif 到程序集 DLL 中并通过以下方式在 WPF 中使用它res 协议 但我在 Visual Studio 2010 2012 中找不到任何项
  • Java:String split():我希望它在末尾包含空字符串[重复]

    这个问题在这里已经有答案了 我有以下字符串 String str nHERE n nTHERE n nEVERYWHERE n n 如果你只是打印这个 它会像这样输出 当然 n不会 字面地 打印 n HERE n n THERE n n E
  • 节点模块是针对不同的节点模块编译的 67

    我有一个在节点模块版本 67 下编译的节点项目 但现在我收到一个错误 它需要节点模块版本 69 usb bindings node was compiled against a different Node js version using
  • 属性“use”在类型“typeof...”上不存在,属性“extend”在类型“typeof”上不存在

    我第一次使用 TypeScript 构建我的 vue 应用程序 但我一直坚持这个Property xxx does not exist on type typeof问题 我研究过类似的问题 但似乎没有一个有效 我在 vue 3 0 0 0
  • 无法编译 WordCount.java

    mark maestro1 usr lib hadoop wordcount classes javac classpath usr lib hadoop hadoop common 2 0 0 cdh4 0 1 jar usr lib h
  • Powershell:返回具有最大编号的文件名

    假设我有一个像这样的文件名列表 Get ChildItem Antarctica Data xls Antarctica Data 03625516 xls Antarctica Data 84327262 xls Antarctica D
  • AspectJ 编织 Maven 模块

    我有一个项目 有多个 Maven 模块 其中之一包含我的方面 我如何获取各个方面并编织多个 Maven 模块 AspectJ Maven 插件的文档有点稀疏 无法找到很多示例 我尝试将aspectj 插件放入父pom 中 但它似乎没有应用其
  • 如何将 Excel 工作簿写入 .NET 中的 MemoryStream?

    如何将 Excel 工作簿写入MemoryStream而不先将其保存到文件系统 内的所有选项Microsoft Office Interop Excel WorkBook保存选项采用文件名 我对 PIA 以及将 Excel 文件存储在文档存
  • “OneHotEncoder”对象没有属性“get_feature_names”

    我正在尝试使用 scikit learn 的 OneHotEncoder 对象的 get feature names 函数提取特征 但它向我抛出一个错误 OneHotEncoder 对象没有属性 get feature names 下面是代
  • 带/不带类的回调函数指针 C++

    我被困 我正在尝试形成一个函数 它将吃掉无类函数指针和对象中的函数指针 这是我当前的代码 希望能解释更多 它应该在 Arduino 上运行 所以我不能使用大型库 首先 我在 Arduino 上使用这个库 SimpleTimer A time
  • Flyway 仅在我的 jar 文件中找不到迁移

    我有类似的问题this https stackoverflow com questions 11226070 flyway not finding the migrations in a jar file但我已经迁移到版本 2 1 1 问题
  • 在应用程序和 Web 之间共享数据库

    我参与了一个必须从现有数据库检索查询的应用程序 该数据库必须在远程服务器中的 php 中实现 并且将是管理员必须将内容插入共享数据库的地方 但我不知道如何正确实现或什么是最佳解决方案 我正在考虑使用 php 创建一个 sqlite 数据库并
  • ld:找不到 -lIOKit.A clang xcode 的库

    我使用项目的 Target 部分将 libIOKit A dylib 库包含到我的 Xcode 项目中 如下图所示 该项目已构建并运行良好 我在编译状态中遇到一些有线错误 ld 找不到 lIOKit 的库 A clang 错误 链接器命令
  • 将 Entity Framework Data First 与 ODBC 和 MySQL 结合使用

    我怎样才能创建一个ADO NET 实体数据模型使用 ODBC 连接 MySQL 我在本地主机上安装了 MySQL 3 51 ODBC 驱动程序 并创建了一个测试 MySQL 数据库 然后使用 VS2013 我使用 ODBC 数据源创建到我的
  • 如何从函数中实时捕获打印内容?

    我想捕捉所有prints 并执行诸如返回它们之类的操作 但继续运行该函数 我找到了这个方法 但它只返回print代码完成后 f io StringIO with redirect stdout f my code return f getv
  • Lua:setfenv() 与 _ENV

    Lua 切换有什么大不了的setfenv to ENV 在各种 新增内容 资料中 这一举措被认为是 Lua 5 1 和 5 2 版本之间最重要的变化之一 但是 PIL 和其他地方给出的示例可以修剪为以下内容 Lua 5 1 Lua 5 2
  • 提交了我的 php 表单,但没有发送任何值

    由于某种原因 这些选项没有显示在我的电子邮件中 我可以很好地发送电子邮件 我可以看到正文及其所有评论 但看不到用户所做的任何条目 我知道我做错了什么 但我无法确定它是什么 另外 如果它看起来很糟糕 请随意嘲笑我 ToEmail email
  • 可以使用存储过程中的动态 SQL 创建 MySQL 触发器吗?

    是否可以使用从存储过程中动态生成的 SQL 在 MySQL 中创建触发器 我通过准备语句在过程中执行其他动态构造的查询 但是当我尝试使用相同的方法创建触发器时 出现以下错误 错误代码 1295 准备好的语句协议尚不支持此命令 From Bu
  • Grepping 查找重叠的模式匹配

    这就是我正在运行的 grep o tcb lt lt lt r t c q c b b 输出是 t b 但我想得到 t c b 我不希望匹配没有前面的 b 或没有后面的 c 因为 tcb 应该在 r t c q b b r t c q b
  • 连接的 Delphi 字符串是否保存在保留对该字符串的引用的隐藏临时变量中?

    我试图了解 Delphi 服务器应用程序中的内存问题 最初我怀疑存在彻底的泄漏 但现在相信我们看到内存挂起的时间比应有的时间长 因为编译器在用 动态连接字符串时使用了隐藏的临时值 导致痛苦的自由空间内存碎片 背景 这是 Windows 上的