Ari*_*The 9 delphi string refcounting delphi-xe2
在Delphi中,函数结果经常被实现为var-parameter(尽管QC票证不是out-parameter).
字符串常量基本上是具有负refcounter的变量,它应该抑制自动内存[de]分配.http://docwiki.embarcadero.com/RADStudio/XE3/en/Internal_Data_Formats#Long_String_Types
它确实压制了它:下面的代码不会泄漏.
type
TDealRecord = record
id_Type: Integer;
Price: extended;
Remark: String;
end;
const const_loop = 100000000;
function TestVar: TDealRecord;
//procedure TestVar;
var
Li: Integer;
LRec: TDealRecord;
begin
for Li := 1 to const_loop do begin
FillChar(Lrec,SizeOf(LRec), 0);
LRec.Remark := 'Test';
// FillChar(Result,SizeOf(Result), 0);
// Result.Remark := 'Test';
end;
end;
Run Code Online (Sandbox Code Playgroud)
但是改变操纵变量 - 它会立即开始大量泄漏.
function TestVar: TDealRecord;
//procedure TestVar;
var
Li: Integer;
LRec: TDealRecord;
begin
for Li := 1 to const_loop do begin
// FillChar(Lrec,SizeOf(LRec), 0);
// LRec.Remark := 'Test';
FillChar(Result,SizeOf(Result), 0);
Result.Remark := 'Test';
end;
end;
Run Code Online (Sandbox Code Playgroud)
事实证明,这string := const
是通过不同的调用实现的,具体取决于LValue:
虽然后两个是按预期克隆指针,但前两个是将字符串复制到新实例,就像我添加UniqueString
调用它们一样.
为何如此差异?
在Delphi中,常量字符串在分配给另一个全局变量时始终被复制,但不会复制到局部变量,以避免在某些边界情况下发生访问冲突.
使用来源,卢克!
从System.pas中查看此代码提取:
{ 99.03.11
This function is used when assigning to global variables.
Literals are copied to prevent a situation where a dynamically
allocated DLL or package assigns a literal to a variable and then
is unloaded -- thereby causing the string memory (in the code
segment of the DLL) to be removed -- and therefore leaving the
global variable pointing to invalid memory.
}
procedure _LStrAsg(var dest; const source);
var
S, D: Pointer;
P: PStrRec;
Temp: Longint;
begin
S := Pointer(source);
if S <> nil then
begin
P := PStrRec(Integer(S) - sizeof(StrRec));
if P.refCnt < 0 then // make copy of string literal
begin
Temp := P.length;
S := _NewAnsiString(Temp);
Move(Pointer(source)^, S^, Temp);
P := PStrRec(Integer(S) - sizeof(StrRec));
end;
InterlockedIncrement(P.refCnt);
end;
....
Run Code Online (Sandbox Code Playgroud)
因此,简而言之,通过设计,并避免在卸载DLL或包并且确实包含一些发送回主进程的常量值时的访问冲突,始终会生成本地副本.
你有两个功能:
LStrAsg
或者UStrAsg
当字符串有机会成为常量时由编译器生成 - 这是上面的代码;LStrLAsg
或者UStrLAsg
(添加L
代表"local"),当源字符串是本地时由编译器生成,因此不是常量:在这种情况下,P.refCnt < 0
将不会被检查,因此它将比上面的代码更快.在与David Heffernan讨论之后,我开始认为 Delphi 编译器只是不知道它分配给变量的值是什么。有点“类型擦除”的地方。它无法区分全局常量和局部堆栈变量和局部字符串表达式。它无法判断函数退出后源是否存在。虽然我们知道它是字符串文字或全局常量或任何具有独立于函数执行的生命周期的东西 - 编译器只是丢失了该信息。相反,它采取防御性的态度,总是克隆价值——只是为了让它不复存在。我不确定,但这看起来很合理。尽管这种粗暴的不加区别的代码生成规则的后果是Delphi 中的又一个陷阱:-(
归档时间: |
|
查看次数: |
2657 次 |
最近记录: |