我正在使用Haskell DLL(GHC版本是8.0.1 x64)的C ++项目中工作。我注意到,正在执行的程序会消耗大量内存。我对此事进行了调查,这就是我所发现的。让我们考虑以下最小示例。这是一个由三个文件组成的小项目。
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
函数时才会引起内存泄漏。
答案 0 :(得分:1)
我决定向ghc-devs邮件列表发送一封电子邮件。 (要阅读所有信件,请参阅the question和其他消息,答案here中可以找到。)
简要说明。内存泄漏是由以下原因引起的:对于每个外部导出,RTS都会创建一个静态C包装器,该包装器在DLL_PROCESS_ATTACH
上初始化,但是在DLL_PROCESS_DETACH
期间没有终结器/析构函数被调用。因此它将一直存在,直到程序终止。