为什么 SetString 在 Delphi 中占用更少的内存(使用 Unicode)?
这是 Delphi 2009,因此适用 Unicode。
我有一些代码将字符串从缓冲区加载到 StringList 中,如下所示:
var Buffer: TBytes; RecStart, RecEnd: PChar; S: string;
FileStream.Read(Buffer[0], Size);
repeat
... find next record RecStart and RecEnd that point into the buffer;
SetString(S, RecStart, RecEnd - RecStart);
MyStringList.Add(S);
until end of buffer
但在一些修改期间,我更改了逻辑,以便最终添加相同的记录,但作为单独派生的字符串而不是通过 SetString 派生,即
var SRecord: string;
repeat
SRecord := '';
repeat
SRecord := SRecord + ... processed line from the buffer;
until end of record in the buffer
MyStringList.Add(SRecord);
until end of buffer
我的内容注意到 StringList 的内存使用量从 52 MB 增加到大约 70 MB。增幅超过 30%。
为了恢复较低的内存使用量,我发现我必须使用 SetString 创建字符串变量以添加到我的 StringList 中,如下所示:
repeat
SRecord := '';
repeat
SRecord := SRecord + ... processed line from the buffer;
until end of record in the buffer
SetString(S, PChar(SRecord), length(SRecord));
MyStringList.Add(S);
until end of buffer
检查和比较 S 和 SRecord,它们在所有情况下都完全相同。但是将 SRecord 添加到 MyStringList 比添加 S 使用更多的内存。
有谁知道发生了什么以及为什么 SetString 节省内存?
跟进。我没想到会这样,但我检查了一下以确定。
既不:
SetLength(SRecord, length(SRecord));
也不
Trim(SRecord);
释放多余的空间。 SetString 似乎需要这样做。
This is Delphi 2009, so Unicode applies.
I had some code that was loading strings from a buffer into a StringList as follows:
var Buffer: TBytes; RecStart, RecEnd: PChar; S: string;
FileStream.Read(Buffer[0], Size);
repeat
... find next record RecStart and RecEnd that point into the buffer;
SetString(S, RecStart, RecEnd - RecStart);
MyStringList.Add(S);
until end of buffer
But during some modifications, I changed my logic so that I ended up adding the identical records, but as a strings derived separately and not through SetString, i.e.
var SRecord: string;
repeat
SRecord := '';
repeat
SRecord := SRecord + ... processed line from the buffer;
until end of record in the buffer
MyStringList.Add(SRecord);
until end of buffer
What I noticed was the memory use of the StringList went up from 52 MB to about 70 MB. That was an increase of over 30%.
To get back to my lower memory usage, I found I had to use SetString to create the string variable to add to my StringList as follows:
repeat
SRecord := '';
repeat
SRecord := SRecord + ... processed line from the buffer;
until end of record in the buffer
SetString(S, PChar(SRecord), length(SRecord));
MyStringList.Add(S);
until end of buffer
Inspecting and comparing S and SRecord, they are in all cases exactly the same. But adding SRecord to MyStringList uses much more memory than adding S.
Does anyone know what's going on and why the SetString saves memory?
Followup. I didn't think it would, but I checked just to make sure.
Neither:
SetLength(SRecord, length(SRecord));
nor
Trim(SRecord);
releases the excess space. The SetString seems to be required to do so.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您连接字符串,内存管理器将分配更多内存,因为它假定您向其中添加越来越多的文本,并为将来的连接分配额外的空间。这样,字符串的分配大小远大于使用的大小(取决于使用的内存管理器)。如果使用SetString,新字符串的分配大小几乎与已使用的大小相同。当SRecord字符串超出范围并且其引用计数变为零时,SRecord占用的内存被释放。因此,您最终会获得字符串所需的最小分配大小。
If you concatenate the string, the memory manager will allocate more memory because it assumes that you add more and more text to it and allocates additional space for future concatenations. This way the allocation size of the string is much larger than the used size (depending on the used memory manager). If you use SetString, the allocation size of the new string is almost the same as the used size. And when the SRecord string goes out of scope and its ref-count becomes zero, the memory occupied by SRecord is released. So you end up with the smallest needed allocation size for your string.
尝试安装内存管理器过滤器(Get/SetMemoryManager),它将所有对 GetMem/FreeMem 的调用传递给默认内存管理器,但它也执行统计数据收集。您可能会发现两种变体的内存消耗是相同的。
只是内存碎片而已。
Try to install memory manager filter (Get/SetMemoryManager), which passes all calls to GetMem/FreeMem to default memory manager, but it also performs stats garhtering. You'll probably see that both variants are equal in memory consumption.
It's just memory fragmentation.