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
相关产品推荐
相关产品推荐

