multithreading - 可以强制释放Delphi threadvar内存吗?

标签 multithreading delphi dll memory-leaks

我一直在寻找 Delphi 2007 for Win32 中构建的 DLL 中似乎存在内存泄漏的问题。如果卸载 DLL 时线程仍然存在(卸载 DLL 时没有对 DLL 的事件调用),则不会释放 threadvar 变量的内存。

问题:是否有某种方法可以使 Delphi 释放与 threadvar 变量关联的内存?这并不只是不使用它们那么简单。许多现有的 Delphi 组件都使用它们,因此即使 DLL 没有显式声明它们,它最终也会使用它们。

一些细节 我已经追踪到 LocalAlloc 调用,该调用是为了响应 threadvar 变量的使用而发生的,这是 Delphi 在 Win32 中围绕线程本地存储的“包装器”。出于好奇,分配调用位于 Delphi 源文件 sysinit.pas 中。相应的 LocalFree 调用仅针对获取 DLL_THREAD_DETACH 调用的线程发生。如果应用程序中有多个线程并卸载 DLL,则每个线程都不会调用 DLL_THREAD_DETACH。 DLL 得到一个DLL_PROCESS_DETACH,除此之外什么也没有;我相信这是预期的并且有效的。因此,在其他线程上进行的任何线程本地存储分配都会被泄漏。

我用一个简短的 C 程序重新创建了它,该程序启动了几个“工作”线程。它在主线程上加载 DLL(通过 LoadLibrary),然后调用工作线程上的导出函数。从 Delphi DLL 导出的函数将值分配给 threadvar 整型变量并返回。然后,C 程序卸载 DLL(通过主线程上的 FreeLibrary)并重复。经过大约 32,000 次迭代后,Process Explorer 中显示的进程内存使用量增长到超过 130MB。我还用umdh更准确地验证了它。 UMDH 显示每个实例丢失 24 个字节。但 Process Explorer 中的 130MB 似乎表明每次迭代约为 4K;我猜测每次都会有一个 4K 片段被泄露,但我不确定。

为了澄清起见,这里是 threadvar 声明和整个导出函数:

threadvar
   threadint : integer;

function Startup( ulID: LongWord; hValue: Longint ): LongWord; stdcall;
begin
   threadint := 123;
   Result := 0;
end;

谢谢。

最佳答案

正如您已经确定的那样,对于从 DLL 分离的每个线程,线程本地存储将会被释放。当 ReasonDLL_Thread_Detach 时,在 System._StartLib 中会发生这种情况。不过,要实现这一点,线程需要终止。线程分离通知在线程终止时发生,而不是在 DLL 卸载时发生。 (如果是相反的情况,操作系统将不得不在某个地方中断线程,以便它可以代表线程插入对 DllMain 的调用。这将是灾难性的。)

DLL应该接收线程分离通知。事实上,这就是 Microsoft 在 how to use thread-local storage with DLLs 的描述中建议的模型。 .

释放线程本地存储的唯一方法是从要释放其存储的线程上下文中调用TlsFree。据我所知,Delphi 将其所有线程变量保留在单个 TLS 索引中,由 SysInit.pas 中的 TlsIndex 变量给出。您可以随时使用该值调用 TlsFree,但您最好确保当前线程中的 DLL 不会再执行任何代码。

由于您还想释放用于保存所有线程变量的内存,因此您需要调用 TlsGetValue 来获取 Delphi 分配的缓冲区的地址。对该指针调用 LocalFree

这将是(未经测试的)Delphi 代码,用于释放线程本地存储。

var
  TlsBuffer: Pointer;
begin
  TlsBuffer := TlsGetValue(SysInit.TlsIndex);
  LocalFree(HLocal(TlsBuffer));
  TlsFree(SysInit.TlsIndex);
end;

如果您需要从主机应用程序而不是从 DLL 中执行此操作,则需要导出一个返回 DLL 的 TlsIndex 值的函数。这样,主机程序可以在 DLL 消失后释放存储本身(从而保证在给定线程中不再执行 DLL 代码)。

关于multithreading - 可以强制释放Delphi threadvar内存吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1071599/

相关文章:

delphi - 是否可以向 delphi 组件添加自定义功能,以便在按下特定键时触发?

Java 多线程与 Shell 脚本

Python 线程队列与多处理管道

c - 使用串行和多线程实现程序蒙特卡罗计算pi

c - 错误 VgTs_WaitSys 在 Valgrind 中意味着什么?

delphi - 在选择时设置组合框文本

delphi - 如何转义 Delphi 中的保留字?

c++ - 在不同的计算机上运行 Qt 应用程序

c# - 外部别名与反射与 System.Addin 的多 DLL 测试?

Visual Studio 2019 能否将所需的 DLL 打包到一个小的 .exe 文件中?