Haskell DLL导致内存泄漏
Haskell DLL causes memory leak
我正在开发一个使用 Haskell DLL 的 C++ 项目(GHC 版本是 8.0.1 x64)。我注意到,正在执行的程序消耗了大量内存。我调查了这件事,这就是我所发现的。让我们考虑以下最小示例。这是一个由三个文件组成的小项目。
HaskellExports.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module HaskellExports where
import Foreign.C.Types
import Foreign.StablePtr
foreign export ccall foo :: CInt -> IO (StablePtr Int)
foo :: CInt -> IO (StablePtr Int)
foo (CInt n) = newStablePtr (fromIntegral n)
包含一个应该从 C/C++ 代码调用的函数。
CWrapper.cpp
#define DLLExport extern "C" __declspec(dllexport)
DLLExport void* c_smth (const int num)
{
return 0;
}
我故意不包括 Haskell 函数的实际导出,因为它们对这个示例没有影响。
main.cpp
#include <Windows.h>
int main()
{
for (;;)
{
HINSTANCE module = ::LoadLibrary(L"HaskellExports.dll");
::FreeLibrary(module);
}
return 0;
}
在这里,在无限循环中,我加载库并立即释放它。让我们尝试以两种不同的方式构建 DLL。首先,我们不要包含 Haskell 目标文件:
ghc -c HaskellExports.hs
ghc -c CWrapper.cpp
ghc -shared -no-hs-main -o HaskellExports.dll CWrapper.o
我已经 运行 程序并注意到(在 Windows 进程监视器的帮助下),内存资源被正确捕获和释放。
现在我们还要添加 Haskell 目标文件:
ghc -c HaskellExports.hs
ghc -c CWrapper.cpp
ghc -shared -no-hs-main -o HaskellExports.dll CWrapper.o HaskellExports.o
我再次 运行 该程序并注意到,它在一段时间内消耗了越来越多的内存而无意释放它。这种情况会导致崩溃。
我做错了什么?
P.S.进一步调查发现只有当HaskellExport.hs
包含至少一个foreign export
函数时才会导致内存泄漏。
我决定向 ghc-devs 邮件列表发送一封电子邮件。 (要阅读所有通信,请参阅 the question and further messages, the answer can be found here)。
简要说明。内存泄漏是由于以下原因造成的:对于每个外部导出,RTS 创建一个静态 C 包装器,它在 DLL_PROCESS_ATTACH
上初始化,但在 DLL_PROCESS_DETACH
期间没有要调用的 finalizer/destructor。所以它会一直存活到程序终止。
我正在开发一个使用 Haskell DLL 的 C++ 项目(GHC 版本是 8.0.1 x64)。我注意到,正在执行的程序消耗了大量内存。我调查了这件事,这就是我所发现的。让我们考虑以下最小示例。这是一个由三个文件组成的小项目。
HaskellExports.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module HaskellExports where
import Foreign.C.Types
import Foreign.StablePtr
foreign export ccall foo :: CInt -> IO (StablePtr Int)
foo :: CInt -> IO (StablePtr Int)
foo (CInt n) = newStablePtr (fromIntegral n)
包含一个应该从 C/C++ 代码调用的函数。
CWrapper.cpp
#define DLLExport extern "C" __declspec(dllexport)
DLLExport void* c_smth (const int num)
{
return 0;
}
我故意不包括 Haskell 函数的实际导出,因为它们对这个示例没有影响。
main.cpp
#include <Windows.h>
int main()
{
for (;;)
{
HINSTANCE module = ::LoadLibrary(L"HaskellExports.dll");
::FreeLibrary(module);
}
return 0;
}
在这里,在无限循环中,我加载库并立即释放它。让我们尝试以两种不同的方式构建 DLL。首先,我们不要包含 Haskell 目标文件:
ghc -c HaskellExports.hs
ghc -c CWrapper.cpp
ghc -shared -no-hs-main -o HaskellExports.dll CWrapper.o
我已经 运行 程序并注意到(在 Windows 进程监视器的帮助下),内存资源被正确捕获和释放。
现在我们还要添加 Haskell 目标文件:
ghc -c HaskellExports.hs
ghc -c CWrapper.cpp
ghc -shared -no-hs-main -o HaskellExports.dll CWrapper.o HaskellExports.o
我再次 运行 该程序并注意到,它在一段时间内消耗了越来越多的内存而无意释放它。这种情况会导致崩溃。
我做错了什么?
P.S.进一步调查发现只有当HaskellExport.hs
包含至少一个foreign export
函数时才会导致内存泄漏。
我决定向 ghc-devs 邮件列表发送一封电子邮件。 (要阅读所有通信,请参阅 the question and further messages, the answer can be found here)。
简要说明。内存泄漏是由于以下原因造成的:对于每个外部导出,RTS 创建一个静态 C 包装器,它在 DLL_PROCESS_ATTACH
上初始化,但在 DLL_PROCESS_DETACH
期间没有要调用的 finalizer/destructor。所以它会一直存活到程序终止。