我有一个桌面程序.大多数操作系统在自己的地址空间中运行程序.
当程序退出时,我相信大多数操作系统释放程序分配的内存并将其返回到内存堆栈以供重用.
我不确定的是,如果程序有内存泄漏,内存"泄漏"是否也会被返回以供重用,或者在重新启动机器之前丢失?
这是我今天早些时候提出的问题的后续问题:小内存泄漏是否重要?,并且有一些评论提到程序存储器在程序完成时被释放.如果在程序完成后释放泄漏,那么它肯定会减轻我的压力,以便严格地消除我的程序中最微小的泄漏.
具体来说,我是一名Windows程序员,我需要知道Windows 98,2000,XP,Vista和7会发生什么(内存丢失或内存释放).但我也想知道Mac和Unix机器上会发生什么.
澄清:我说的是不增长的泄漏.这些是在程序运行时发生的一次大小的泄漏.
我确实意识到程序中不断增长的泄漏是严重的,必须加以修复.
无论哪种方式,问题不在于是否必须修复泄漏.当程序结束时,操作系统是否会返回泄漏的内存.
我正在使用Delphi 2009.我有一个非常简单的数据结构,有2个字段:
困难在于我可能有几百万条记录,因此它们的总大小可能超过10 GB.显然,我正在寻找一种磁盘解决方案,而不是内存解决方案.
我的程序需要根据关键字段随机检索这些记录.这就是需要尽可能高效的部分.
我应该将数据库用于这样一个简单的结构吗?如果是这样,哪个数据库最好处理这个并且最简单的实现?
或者,是否有一个简单的磁盘上数据结构,不需要一个完整的数据库,也可以工作?
好吧,我所需要的只是让我回到现实的一个答案.我一直在寻找比简单数据库更简单的东西.但是,如果不使用数据库,那么我意识到我已经用自己对另一个问题的答案回答了这个问题:小应用程序和工具的最佳数据库.
我的回答是DISQLite3为我指定有原因.这就是我实施的可能性.
一些可能的更好的答案.那很棒.我将能够尝试一些不同的方法来看看什么效果最好.
更多的考虑,我不得不改变GpStructuredStorage解决方案的接受答案.
就我而言,总计几千兆字节的一百万条记录将对数据库结构造成压力.具体来说,用于在大多数数据库中存储索引的B*树速度很快,但对于某些操作(如重新索引一百万个值)会减慢.
对于索引,你唯一能找到比B*更快的东西就是哈希表.这正是gabr建议添加到GpStructuredStorage解决方案中所提供的内容.我认为它将哈希值分段为一个4级目录结构的方式非常优雅.
我可以使用哈希解决方案的关键原因是我只需要通过密钥随机访问.我不需要按键排序.如果需要排序,则哈希表的速度增益将会丢失,数据库系统将成为无脑的赢家.
当我开始实现这个时,我应该对这个技术与数据库进行比较.也许我会与Firebird和SQLite进行比较,这两者都是值得对手的.
另外一个跟进:
我刚刚发现了A. Bouchez的Synopse Big Table,它专为速度而设计,几乎可以准确地满足我的问题规格.当我在几个月内完成实施时,我会先尝试一下,然后在这里报告我的结果.
稍后的后续(2015年7月)
我从未尝试过Synopse Big Table.到目前为止,我一直坚持使用我的B*树.但现在我升级到Delphi XE8并计划使用FireDAC和SQLite来使用数据库解决方案.
我正在使用带有Unicode字符串的Delphi 2009.
我正在尝试编码一个非常大的文件,将其转换为Unicode:
var
Buffer: TBytes;
Value: string;
Value := Encoding.GetString(Buffer);
Run Code Online (Sandbox Code Playgroud)
这适用于40 MB的缓冲区,其大小加倍并返回值为80 MB的Unicode字符串.
当我尝试使用300 MB缓冲区时,它会给我一个EOutOfMemory异常.
嗯,这并非完全出乎意料.但无论如何我决定追踪它.
它进入系统单元中的DynArraySetLength过程.在该过程中,它进入堆并调用ReallocMem.令我惊讶的是,它成功分配了665,124,864字节!
但是在DynArraySetLength结束时,它调用FillChar:
// Set the new memory to all zero bits
FillChar((PAnsiChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
Run Code Online (Sandbox Code Playgroud)
你可以通过评论看到应该做什么.该例程并不多,但这是导致EOutOfMemory异常的例程.这是系统单元的FillChar:
procedure _FillChar(var Dest; count: Integer; Value: Char);
{$IFDEF PUREPASCAL}
var
I: Integer;
P: PAnsiChar;
begin
P := PAnsiChar(@Dest);
for I := count-1 downto 0 do
P[I] := Value;
end;
{$ELSE}
asm // Size = 153 Bytes
CMP EDX, 32
MOV …Run Code Online (Sandbox Code Playgroud) 我的Delphi程序中有一些很长但很简单的循环,它可能循环数百万次并需要几秒钟才能执行.循环内部的代码非常快并且已经过优化.这需要很长时间,因为它已经完成了很多次.
例如:
Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
{ do something simple with R.Value }
R := R.NextRecord;
end;
Screen.Cursor := crDefault;
Run Code Online (Sandbox Code Playgroud)
现在我不希望我的程序没有响应,所以我想在循环中添加一个Application.ProcessMessages.但我也希望添加的语句尽可能少地减慢循环速度.
我正在关注一个链表,所以我甚至没有可用的计数变量,如果我想要间隔,则必须添加一个.或者我必须添加一个计时器,但需要最小化时间检查.
我应该如何实现这一点,以尽量减少增加的开销?
结论:
现在,我正在做类似APZ28的回答.
但看起来长期我应该实现某种线程来处理这个问题.感谢您指出这一点,因为我认为Application.ProcessMessages是唯一的方法.
我Mouse_Event在Delphi 2009 中使用了这个函数,但是Delphi的文档说这个函数已被取代并SendInput代替使用.
Delphi SendInput文档定义了语法和参数,但没有示例,也不清楚如何使用该函数.我在网上浏览过,找不到任何好的Delphi示例.
具体来说,我试图模拟左鼠标然后向上.目前我这样做Mouse_Event如下:
Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Run Code Online (Sandbox Code Playgroud)
我该如何使用SendInput?
跟进:
我最终留下了我的代码,就像@David建议的那样.
但我给了@ opc0de答案,因为他确实回答了我的问题.但是,我不能保证它是正确的,因为我从来没有尝试过.
我需要与问题“如何获取控件上的光标位置?”相反的信息。问道。
给定当前光标位置,如何找到表单(在我的应用程序中)和光标当前所在的控件?我需要它的句柄以便我可以使用Windows.SetFocus(Handle).
作为参考,我使用的是 Delphi 2009。
我正在做一个简单的StringList.sort,但是Delphi使用的QuickTort不是一个稳定的排序,这意味着它可能会改变具有相同键的记录的相对顺序.
我需要使用稳定的排序.对我来说,实现这个最简单的方法是什么?
Mike W的答案可能是最简单的方法,无需进行太多的代码更改.
谢谢,迈克.
我只是无法弄清楚EurekaLog为我的程序报告的内存泄漏.我正在使用Delphi 2009.这是:
Memory Leak: Type=Data; Total size=26; Count=1;
The stack is:
System.pas _UStrSetLength 17477
System.pas _UStrCat 17572
Process.pas InputGedcomFile 1145
Run Code Online (Sandbox Code Playgroud)
这就是堆栈中的全部内容.EurekaLog指向我首先分配未释放内存的位置.根据它,我的程序中的行是InputGedcomFile的第1145行.那条线是:
CurStruct0Key := 'HEAD' + Level0Key;
Run Code Online (Sandbox Code Playgroud)
其中CurStruct0Key和Level0Key在过程中简单定义为本地变量,Delphi内存管理器在进入和离开过程时应该动态处理:
var CurStruct0Key, Level0Key: string;
Run Code Online (Sandbox Code Playgroud)
所以现在我看一下系统单元中的_UStrCat过程.17572行是:
CALL _UStrSetLength // Set length of Dest
Run Code Online (Sandbox Code Playgroud)
然后我转到系统单元中的_UStrSetLength过程,相关的行是:
@@isUnicode:
CMP [EAX-skew].StrRec.refCnt,1 // !!! MT safety
JNE @@copyString // not unique, so copy
SUB EAX,rOff // Offset EAX "S" to start of memory block
ADD EDX,EDX // Double length to get size
JO @@overflow
ADD EDX,rOff+2 // Add string …Run Code Online (Sandbox Code Playgroud) 在Delphi中更新状态栏需要花费很长时间!
示例:我搜索文件并显示状态栏中找到和搜索的文件数:
OwnerForm.StatusBar1.SimpleText
:= Format('Searching (%d found in %d files) ...', [NumFound, Total]);
Run Code Online (Sandbox Code Playgroud)
对于每200次更新状态栏,搜索大约增加1秒的时间.
有没有办法减少这种过多的开销,但仍然更新用户的状态?
我有一个Delphi XE8程序,我使用Office Automation来构建Excel文件.该建筑需要几秒钟,所以我将这项工作放入后台线程,以便我的程序保持响应,我可以处理取消请求.
我这样设置了它:
TTask.Run(
procedure
var
oXL, oSheet, o2Sheet, oRng, VArray : Variant;
begin
oXL := CreateOleObject('Excel.Application');
oXL.Visible := False;
oXL.DisplayAlerts := false;
... (all the processing to build the Excel file)
oXL.Application.Workbooks[1].SaveAs(ExcelFilename, 51 { = xlWorkbookDefault } );
oXL.Workbooks.Close;
oXL.Application.Quit;
oXL := Unassigned;
end
);
Run Code Online (Sandbox Code Playgroud)
这段代码完全正常,就像我想要的那样.它创建了Excel工作簿作为后台任务,完成后从任务返回到主线程,并且应用程序保持响应.
我以为一切都很棒.它在我的Windows 10计算机上运行Office 2016.我有几个用户也在他们的计算机上成功运行它.
但是我收到了一个用户的报告,其中程序在线程中挂起.我能够做一些测试.当代码没有在他的机器上的一个线程中运行时,它工作正常(当然,应用程序不再保持响应,我无法处理取消请求).
我能告诉用户机器唯一不同的是他们运行的是Windows 7和Office 2007.
我并非100%确定Office Automation是线程安全的.我可以把它全部放到一个线程中,理解一些自动化命令(哪些?)可能需要同步?如果它不是线程安全的,那么在生成Excel文件时我还能如何保持应用程序的响应?
是不是Windows 7上的Office Automation与Windows 10或Office 2007与Office 2016之间的差异导致旧版本中的任务失败?如果是这样,有解决方法吗?
delphi ×9
optimization ×2
automation ×1
database ×1
delphi-2009 ×1
encoding ×1
excel ×1
focus ×1
large-files ×1
memory-leaks ×1
mouse-cursor ×1
mouseevent ×1
overhead ×1
sendinput ×1
setfocus ×1
sorting ×1
stable-sort ×1
statusbar ×1
tstringlist ×1
unicode ×1