热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

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

如何解决《串联的Delphi字符串是否保存在保留对字符串的引用的隐藏临时变量中?》经验,为你挑选了1个好方法。

我试图了解Delphi服务器应用程序中的内存问题:最初我怀疑彻底泄漏,但是现在相信,由于编译器在使用+动态连接字符串时使用了隐藏的临时文件,因此内存的闲置时间比应有的更长。 ,造成痛苦的自由空间内存碎片。

背景:

这是Windows上的一整套32位服务器应用程序,Delphi版本已经很老了,我认为它是7,但可以肯定是Unicode之前的版本,并使用Nexus 3内存管理器在其中编写了DLL来挂接所有分配/ free调用(以及GB的内存跟踪)。

我有应用程序源代码,但没有编译器;我不是该应用程序的开发人员(甚至不是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() 接受一个const字符串参数并将其挂起,以便系统的其他部分知道此线程在做什么。

我对代码的反汇编显示,编译器已分配了一个隐藏的本地临时变量,以接收“工作中”部分和任务名称部分的串联,这是传递给的SetThreadName地方,它还保留了字符串的句柄。

当任务正在运行时-可能是20分钟-我相信该字符串有两个句柄。一个保存在内部SetThreadName,另一个保存在隐藏的临时文件中。

一切都很好。

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

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

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

对于大多数过程而言,这无关紧要,因为它们的开始和结束都相对接近,因此所有内容都可以一次发布,但是在循环服务器应用程序中,这些过程可能会停留更长的时间。这导致我们内存碎片。

变得更糟

在实际的应用程序中,它更像是:

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

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

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

我敢肯定FormatDateTime,在任务完成后很长一段时间里,我就已经看到字符串结果在内存中徘徊了,我已经看到它实际上是一个大约30字节的分配,位于一个1兆字节的内存部分的中间,周围是可用空间; Nexus3MM用于VirtualAlloc分配更大的OS级块。

这单30字节的字符串最终被释放,无论是在下一循环或当程序退出,所以我敢肯定这不是一个泄漏,但我宁愿是一个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中间结果(我已经看到了)来完成此操作,但是不确定整体串联。

这只是感觉不对。

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



1> Ken Bourassa..:

根据我的经验,它确实可以像这样工作。我不确定这是通过合同还是通过实施。我猜想随着最近增加的内联变量声明,现在可能有所不同。但是我相信在preunicode的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;

另外,您可能需要检查此问题以获取其他见解。


推荐阅读
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • windows便签快捷键_用了windows十几年,没想到竟然这么好用!隐藏的功能你知道吗?
    本文介绍了使用windows操作系统时的一些隐藏功能,包括便签快捷键、截图功能等。同时探讨了windows和macOS操作系统之间的优劣比较,以及人们对于这两个系统的不同看法。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Win10下游戏不能全屏的解决方法及兼容游戏列表
    本文介绍了Win10下游戏不能全屏的解决方法,包括修改注册表默认值和查看兼容游戏列表。同时提供了部分已经支持Win10的热门游戏列表,帮助玩家解决游戏不能全屏的问题。 ... [详细]
  • 如何在联想win10专业版中修改账户名称
    本文介绍了在联想win10专业版中修改账户名称的方法,包括在计算机管理中找到要修改的账户,通过重命名来修改登录名和属性来修改显示名称。同时指出了windows10家庭版无法使用此方法的限制。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 电脑公司win7剪切板位置及使用方法
    本文介绍了电脑公司win7剪切板的位置和使用方法。剪切板一般位于c:\windows\system32目录,程序名为clipbrd.exe。通过在搜索栏中输入cmd打开命令提示符窗口,并输入clip /?即可调用剪贴板查看器。赶紧来试试看吧!更多精彩文章请关注本站。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
author-avatar
冷尔岚钰_820
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有