在Windows 7中从托管代码调用非托管代码时发生内存泄漏问题

8
当我从C#代码调用非托管的C++代码时,似乎存在某种内存泄漏问题。C++使用ifstream.read从文件中读取数据,并将其写入Vector中。
这只在升级到Windows 7后发生,而在Vista上没有发生,但如果我使用在Vista上编译的本地dll版本,也不会改变任何东西!如果我直接运行相同的C++代码,而不是通过托管互操作性,就没有内存泄漏问题!如果我在vshost进程中运行托管进程,则没有内存泄漏问题!
以下是调用签名:
        [DllImport(DllPath, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.I1)]
    public static extern bool MyMethod(
        int x, 
        string  y, 
        string  z, 
        bool    v, 
        bool    w);

以及本地化的版本:

MyDll_Export bool APIENTRY MyMethod(
int x,
const wchar_t*  y, 
const wchar_t*  z,
bool v,
bool w)

当我从C++中调用它时,我会这样调用:
MyMethod(1, L"My String 1", L"My String 2", true, true)

当我查看托管和非托管内存的性能计数器时,我发现所有内存都来自非托管代码。考虑到封送是非常简单的,我不明白为什么直接调用 C++ 和通过 C# 调用之间会有差异。我也不知道为什么这只会在 Windows 7 上发生(两个 Windows 安装都安装了 .net 3.5 SP1)。
有人知道原因吗?
另外,如果有人知道一个适用于 Window 7 的本机内存分析工具,我会很高兴知道(目前我只是将所有显式内存分配打印到控制台,没有任何区别)。

LeakDiag [http://mcfunley.com/277/using-leakdiag-to-debug-unmanaged-memory-leaks] 或者AutomatedQA的AQTime可以进行非托管内存泄漏分析。 - Lou Franco
你使用哪个性能计数器来测量内存消耗? - Peter Mortensen
您已经标记了问题“vshost.exe”。在测量内存消耗时,应用程序是否在 Visual Studio 下运行? - Peter Mortensen
@Lou: 在Windows 7上都不起作用。 @Peter: 我检查了本地分配的“进程-私有字节”和托管的CLR内存。 我标记了vshost.exe,因为“如果我在vshost进程中运行托管进程,则没有内存泄漏”,而常规进程的内存测量值下降了。 - Meidan Alon
4个回答

5
我确定问题与将C#数据类型转换为它们的C++对应项有关。由于您将返回值bool转换为带符号的1字节值,也许您应该对函数参数执行相同的操作?C# bool类型是4个字节,也许您在这里泄漏了?此外,指定字符串的非托管类型可能会有所帮助。
[DllImport(DllPath, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool MyMethod(
        int x,
        [MarshalAs(UnmanagedType.LPWStr)]
        [In] string y,
        [MarshalAs(UnmanagedType.LPWStr)]
        [In] string z,
        [MarshalAs(UnmanagedType.I1)]
        bool v,
        [MarshalAs(UnmanagedType.I1)]
        bool w);

解释给评论者:

对于C++中的bool类型:

通常,零或空指针值会转换为false,任何其他值都会转换为true。

...

1998年的C++标准库为bool定义了vector模板的特化。该类的描述表明,实现应将元素打包,以便每个bool只使用一位内存。

因此,无论您使用什么值,都将得到一个值为true或false的c ++布尔值。


如果布尔值的大小不匹配,它不会导致内存泄漏,但更可能是访问冲突或某种堆栈损坏。 - user7116
不错的文件Scott,C#将bool类型封送为4字节。 v和w缺少marshalas属性。 http://blogs.msdn.com/oldnewthing/archive/2009/08/13/9867383.aspx - Hasani Blackwell
谢谢,这是一篇不错的阅读材料,但并没有帮助到我。正如我在另一条评论中提到的,我只调用了这个方法一次,所以即使有泄漏,也不会被注意到。 - Meidan Alon
@Meidan Alon,当您调用该方法时,您泄漏了多少内存? - scottm
我已经在两个时间点进行了测量。没有内存泄漏时,私有字节为37k和66k;有泄漏时,它达到了90k和1.7G。 因此,它增长得非常快! - Meidan Alon
在这个阶段,我认为你将不得不增加关于托管代码的更多信息。我猜测你的非托管代码没有泄漏的原因是泄漏不在这个函数中。 - scottm

2
很遗憾,一旦涉及字符串,就没有简单的编组。为了帮助您追踪问题,我们需要更多的数据。您可以提供以下信息吗?
原生方法签名、本地代码中字符串的内存管理方式以及使用API时的C++示例。
编辑:尝试以下签名。这告诉CLR仅在一个方向上传递数据,而不是在两个方向上进行内存编组。
    [DllImport(DllPath, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.I1)]
    public static extern bool MyMethod(
            int x, 
            [In] string  y, 
            [In] string  z, 
            bool    v, 
            bool    w);

[In] 没有起到帮助作用,为什么在 Vista 和 Windows 7 之间会发生变化呢? 另外,我没有提到我只调用这个方法一次。 - Meidan Alon

1

在查找内存泄漏时,我发现使用CLR Profiler非常有帮助。


0
你确定有内存泄漏吗?
你基于什么来确定是否存在内存泄漏?你说你可以从性能计数器中看到它,但实际上你观察到了什么?你看到的是持续上升的曲线,还是一个稳定在高水平上的曲线?高内存消耗经常被误解为内存泄漏。
顺便问一下,你能同时发布你的 C++ 函数定义吗?

我看到一个不断上升的曲线。 如果您指的是函数体,那么它只是使用ifstream.read从文件中读取数据的C++函数定义。 - Meidan Alon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接