首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi String 內存結構揭秘:引用計數、長度前綴、#0 終結符與 Copy-on-Write 機制

     編輯:Delphi

Delphi 字串內存佈局與引用計數機制深度解析

Delphi 的 string (AnsiString/UnicodeString) 採用 **引用計數 + Copy-on-Write (COW)** 機制,內存佈局包含:長度前綴、字元資料、#0 終結符、引用計數。理解這套機制能解釋字串賦值、修改、參數傳遞的行為差異,並指導高效能編程。

字串內存佈局圖解

[引用計數: 4 Bytes] [長度: 4 Bytes] [字元資料...] [#0: 1 Byte]
       ↑                    ↑               ↑
    str-8                str-4             str (指針指向這裡)
  • 綠色區 (引用計數):多少個字串變數指向此內存塊
  • 藍色區 (長度):字元數量 (不含 #0)
  • 黃色區 (字元資料):實際字元內容
  • 紅色區 (#0):兼容 C/API 的空字元終結符

指標地址與實際地址的區別


var
  str: string;
  pstr: PString;  // ^string
  pc: PChar;
begin
  // 未賦值前:棧上指標存在,堆上內存為 nil
  ShowMessage(IntToStr(Integer(@str)));  // 棧上指標地址
  ShowMessage(IntToStr(Integer(str)));   // 0 (nil)

  str := 'Delphi';
  // 賦值後:棧上指標不變,堆上分配新內存
  ShowMessage(IntToStr(Integer(@str)));  // 同前
  ShowMessage(IntToStr(Integer(str)));   // 堆上實際地址 (如 $4580800)

  // 通過指標地址讀取
  pstr := @str;
  ShowMessage(pstr^);  // 'Delphi'

  // 通過實際地址讀取
  pc := PChar(Integer(str));
  ShowMessage(pc);     // 'Delphi'
end;

引用計數與 Copy-on-Write 實戰演示


var
  str, s1, s2: string;
  pint: PInteger;
begin
  str := Self.Caption;  // 假設 'Form1' (長度 5)
  s1 := str;            // 引用計數 +1
  s2 := str;            // 引用計數 +1

  // 三個變數指向同一塊堆內存
  ShowMessage(IntToStr(Integer(str)));  // 同一地址
  ShowMessage(IntToStr(Integer(s1)));   // 同一地址
  ShowMessage(IntToStr(Integer(s2)));   // 同一地址

  // 讀取長度 (前移 4 Bytes)
  pint := PInteger(Integer(str) - 4);
  ShowMessage(IntToStr(pint^));  // 5

  // 讀取引用計數 (前移 8 Bytes)
  pint := PInteger(Integer(str) - 8);
  ShowMessage(IntToStr(pint^));  // 3

  // 修改 s1 觸發 COW:複製內存、原塊引用計數 -1、新塊引用計數 = 1
  s1 := 'NewForm';  // 此時 str/s2 仍指向 'Form1',s1 指向新內存 'NewForm'
end;

特殊現象:常量與區域變數引用計數為 -1


const
  CSTR = 'Constant';
var
  LocalStr: string;
begin
  LocalStr := 'Local';
  // 測試發現:常量、非全域區域變數引用計數常為 -1
  // 表示「不參與引用計數管理,永不釋放」或「編譯器優化標記」
end;

引用計數狀態轉移圖

效能優化啟示

  1. 避免不必要的字串複製:多用 const 參數傳遞 const S: string,不增引用計數
  2. 大量拼接用 StringBuilder:TStringBuilder 避免中間字串分配
  3. 跨執行緒傳遞字串:引用計數非原子操作,需加鎖或複製 string(s) 強制獨佔
  4. 常量字串優先:編譯期確定、無引用計數開銷、唯讀安全

偵錯技巧:查看字串內部狀態


procedure DumpStringInfo(const Name: string; const S: string);
var
  RefCount, Len: Integer;
begin
  if Pointer(S) = nil then
    WriteLn(Name, ' = nil')
  else
  begin
    RefCount := PInteger(Integer(S) - 8)^;
    Len := PInteger(Integer(S) - 4)^;
    WriteLn(Format('%s: Addr=%p Len=%d RefCount=%d Data="%s"',
      [Name, Pointer(S), Len, RefCount, S]));
  end;
end;

掌握字串內存佈局與 COW 機制,不僅能解決疑難雜症 (如多執行緒字串損壞),更能寫出高效能、低記憶體壓力的 Delphi 代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
操作原內存塊引用計數新內存塊引用計數說明
聲明變數--棧上指標 = nil
賦值字面值/運算式-1分配新塊
變數賦值 (s1 := s2)+1-共享同一塊
修改字串 (s1 := 'x')-11COW 複製新塊
修改字元 (s1[i] := 'x')-11COW 複製新塊 (Delphi 2009+)
離開作用域 / Free-1-歸零則釋放內存