You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Object Pascal单链表析构器为何总有一块内存未释放?

Object Pascal单链表析构器遗留内存块问题

在Object Pascal中实现单链表类的析构器时,始终会遗留一块未释放的内存块,即使将递归析构器替换为迭代实现,问题依然存在。以下是相关代码、编译指令及运行结果:

文件 LinkedListMWI.pas

{$mode objfpc}{$H+}{$J-}

unit LinkedListMWI;

interface

uses SysUtils; 

type
  TNode = class
  private
    var
      FData: Integer;
      FNext: TNode;
  public
    constructor Create(Data: Integer);
    destructor Destroy(); override;
  end;

  TLinkedList = class
  private
    var
      FHead: TNode;
  
  public
    constructor Create(Items: Array of Integer);
    destructor Destroy(); override;
  end;

implementation

constructor TNode.Create(Data: Integer); 
begin
  inherited Create();
  Self.FData := Data;
  Self.FNext := Nil;
end;

destructor TNode.Destroy();
begin
  FreeAndNil(Self.FNext);
  inherited Destroy();
end;

constructor TLinkedList.Create(Items: Array of Integer);
var
  Head, Last, ThisNode: TNode;
  ThisItem: Integer;
begin
  inherited Create();
  Head := Nil;
  for ThisItem in Items do
  begin
    ThisNode := TNode.Create(ThisItem);
    if not Assigned(Head) then
    begin
      Head := ThisNode;
      Last := Head;
    end
    else
    begin
      Last.FNext := ThisNode;
      Last := ThisNode;
    end;
  end;
  Self.FHead := Head;
end;

{ TODO Still one block unfreed }
destructor TLinkedList.Destroy();
begin
  FreeAndNil(Self.FHead);
  inherited Destroy;
end;

end.

文件 MWI.lpr

{$mode objfpc}{$H+}{$J-}

program mwi;

uses SysUtils, LinkedListMWI;

var 
  Nums: Array of Integer = (1, 2, 3, 4, 5, 6, 7);
  List: TLinkedList;
begin
  List := TLinkedList.Create(Nums);
  FreeAndNil(List);
end.

编译指令

fpc -vwhine -glh MWI.lpr

运行结果

15 memory blocks allocated : 1524/1536
14 memory blocks freed     : 1496/1504
1 unfreed memory blocks : 28
True heap size : 360448
True free heap : 360192
Should be : 360224
Call trace for block $0000FFFF80105500 size 28
  $0000000000400210  main,  line 11 of MWI.lpr
  $0000000000400168

问题分析与解决方案

核心原因

原代码的递归释放逻辑本身是正确的,所有节点和链表对象都应该被正常释放。你看到的“未释放内存块”大概率是FPC运行时库的内部分配(比如异常处理框架、动态数组元数据等),这类内存会在程序结束时被操作系统自动回收,不属于真正的内存泄漏。不过递归释放存在栈溢出风险,建议改用迭代方式释放节点。

修复方案:迭代释放所有节点

修改TNode和TLinkedList的析构器,手动迭代释放每个节点,避免递归:

// 修改TNode的析构器,移除递归释放逻辑
destructor TNode.Destroy();
begin
  inherited Destroy();
end;

// 修改TLinkedList的析构器,迭代释放所有节点
destructor TLinkedList.Destroy();
var
  Current, NextNode: TNode;
begin
  Current := FHead;
  while Assigned(Current) do
  begin
    NextNode := Current.FNext; // 提前保存下一个节点
    Current.Free;              // 释放当前节点
    Current := NextNode;       // 切换到下一个节点
  end;
  FHead := nil;
  inherited Destroy;
end;

验证结果

编译运行修改后的代码,如果仍然显示有一块未释放内存,可确认是FPC运行时的内部分配,无需担心。若要更精确跟踪内存,可使用FPC的heaptrc单元(编译参数-gh已包含)进行详细分析。

额外说明

递归释放节点在链表长度较大时可能触发栈溢出,迭代释放是更安全、稳定的实现方式,尤其适用于处理大规模数据。

内容的提问来源于stack exchange,提问作者musarithmia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 18:35:10