处理带有内存泄漏的非托管DLL

时间:2014-05-06 16:58:32

标签: c# memory-management memory-leaks unmanaged appdomain

我有一个c#应用程序,它依赖于第三方非托管程序集来访问某些硬件。

非托管代码存在内存泄漏,每次访问后内存消耗会增加~10mb。问题是众所周知的;没有bugfix可用。

有没有办法可以在没有定期重启的情况下继续使用这个程序集?

我尝试创建一个单独的AppDomain,通过appDomain.CreateInstanceAndUnwrap()将违规代码加载到该AppDomain中,然后通过AppDomain.Unload()卸载域。但是,这显然不会释放该域使用的非托管内存,只释放托管内存。

我还可以将应用程序拆分为两个独立的部分,并仅重新启动具有非托管dll的部分。然而,这将意味着重大的重新设计,并可能导致大量的减速,因为必须在这两个部分之间交换大量数据。

是否有另一种方法可以驯服这个漏出的程序集并强制它释放内存而不重新启动。?

2 个答案:

答案 0 :(得分:4)

您描述的方式是,dll正在分配非托管内存。不幸的是,这种记忆不会受到卸载appdomain的影响,正如您已经发现的那样。

你有几个选择,但我认为其中任何一个都没有吸引力:

  1. 您可以在主应用程序中继续使用dll。您可以建议您的用户/客户他们需要有大量可用内存,但需要不时重新启动应用程序。您可能希望在程序实际开始崩溃之前检测到内存不足的情况,轻轻地刺激用户重新启动它,而不是仅仅因为异常而崩溃。
  2. 您可以尝试深入了解dll的数据结构。如果dll实际上正在发生变化(即新版本即将发布),我会强烈建议您在作者身上努力解决内存泄漏问题。但是,这似乎不太可能,因为我很确定如果出现新版本,泄漏修复。因此,将代码直接绑定到该dll的内部可能是 解决方案。访问引用非托管内存的实际指针可能允许您根据需要手动释放它。
  3. 您可以将问题隔离到单独的进程中。更多内容如下。
  4. 您可以重新实现dll的功能。
  5. 这里可能有第5或第6个选项,但是认为上面的4个选项涵盖了我想到的东西。

    关于将其分离为一个单独的过程,这是我首先尝试做的事情:

    我会启动一个进程,并使用您可以找到的最快的进程内通信通道向它发送请求。管道似乎很合适,或者是内存映射文件。

    然后,您将在该单独的过程中检测内存不足的情况,希望有点早,以便您可以建议主程序应该考虑启动替换过程。

    主要流程可以这样做,但不是等待其他流程完全启动,它可以继续向即将死亡的实例提供更多请求,填补它更多,在切换到新实例并要求旧实例终止之前。

    这样可以最大限度地减少停机时间,但会在转换过程中暂时保留额外的进程。

    所有这些都取决于你拥有的实际场景。如果你需要每秒100秒或1000次调用这个dll,那么在任何情况下进行进程内通信都可能是不可行的。

答案 1 :(得分:1)

DLL在您的进程中分配非托管内存,但不释放它。在这种情况下,卸载应用程序域显然没有帮助,因为内存是在进程内分配的,而不是在应用程序域内。

为了摆脱内存泄漏,你必须释放不同的内存,有两种方法可以做到这一点 - 终止进程并让操作系统自行完成或自由释放。为了自己释放它,你必须通过DLL的公共接口获取它,通过DLL的私有实现细节或通过检查进程的堆来获取内存的句柄。它可能是可行的,但我想它既不好玩也不容易,甚至可能都不健壮。

相比之下,具有两个流程并定期重新启动泄漏流程的解决方案似乎既简单又强大。我不会担心性能,因为您可以使用memory mapped files来共享数据并避免复制它。

最简单的解决方案当然是修复内存泄漏,甚至可以通过修复源代码(如果可用)或修补DLL来自行修复内存。