Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ref string[]内存泄漏与NET/COM互操作_C#_.net_Memory Leaks_Interop - Fatal编程技术网

C# ref string[]内存泄漏与NET/COM互操作

C# ref string[]内存泄漏与NET/COM互操作,c#,.net,memory-leaks,interop,C#,.net,Memory Leaks,Interop,我最近发现了一个非常奇怪的(对我来说)从C#使用的COM对象内存泄漏。具体地说,使用字符串数组调用IEnumString.Next方法,该字符串数组已经包含由上一次调用产生的值,从而导致内存泄漏 IEnumString在C#端看起来像这样: 像这样调用RemoteNext(Next)方法会导致泄漏,通过长时间重复运行并看到“Private Bytes”计数器不断上升来验证 string[] item = new string[100]; // OBS! Will be re-used for e

我最近发现了一个非常奇怪的(对我来说)从C#使用的COM对象内存泄漏。具体地说,使用字符串数组调用IEnumString.Next方法,该字符串数组已经包含由上一次调用产生的值,从而导致内存泄漏

IEnumString在C#端看起来像这样:

像这样调用RemoteNext(Next)方法会导致泄漏,通过长时间重复运行并看到“Private Bytes”计数器不断上升来验证

string[] item = new string[100]; // OBS! Will be re-used for each call!
for (; ; )
{
    int fetched;
    enumString.RemoteNext(item.Length, item, out fetched);
    if (fetched > 0)
    {
        for (int i = 0; i < fetched; ++i)
        {
            // do something with item[i]
        }
    }
    else
    {
        break;
    }
}
编辑2:在调用RemoteNext之前,只需向字符串数组添加一个字符串值(仅包含空值),也可以在第二个非泄漏情况下显示泄漏

string[] item = new string[100]; // Create a new instance for each call.
item[0] = "some string value"; // THIS WILL CAUSE A LEAK
enumString.RemoteNext(item.Length, item, out fetched);

因此,似乎项数组必须为空,封送层才能正确释放复制到其中的未管理字符串。即使如此,数组仍将返回相同的值,即使用非空数组不会导致返回错误的字符串值,它只是泄漏了一些值。

IEnumString
-它只是一个接口。底层COM对象是什么?这是主要嫌疑犯

查看
IEnumString
的非托管声明:

HRESULT Next(
  [in]   ULONG celt,
  [out]  LPOLESTR *rgelt,
  [out]  ULONG *pceltFetched
);
如您所见,第二个参数
rgelt
只是一个指向字符串数组的指针-没有什么特别的,但是当您执行托管调用时

string[] item = new string[100]; // Create a new instance for each call.
item[0] = "some string value"; // THIS WILL CAUSE A LEAK
enumString.RemoteNext(item.Length, item, out fetched);
项[0]
中的字符串似乎已转换为未正确释放的LPOLESTR。因此,请尝试以下方法:

string[] item = new string[1];
for (; ; )
{
    int fetched;
    item[0] = null;
    enumString.RemoteNext(1, item, out fetched);
    if (fetched == 1)
    {
       // do something with item[0]
    }
    else
    {
        break;
    }
}

注意合成测试,这种测试会运行相同的代码片段数百万次。使用Perfmon.exe并观察测试进程的垃圾回收次数。COM使用非托管内存,它不会给GC带来很大压力。这不是一个综合测试,我不仅运行了显示的代码片段,还运行了它所使用的整个程序段。(这是一个小型测试应用程序,检索服务器应用程序的名称空间(分支和叶)并简单地打印出来。这是一次又一次地完成的。如果让第一个代码段运行,进程很快将消耗数百MB的内存,而第二个代码段将稳定在20MB左右)。我还使用Process Explorer检查了GC堆内存的使用情况,在这两种情况下都没有消耗太多内存,因此我非常确信是未管理的COM内存泄漏。是的,你当然是对的!我没有考虑编组层将已经存在于项目(RGELT)数组中的托管字符串转换为需要释放的非托管COM内存。错误不在于IEnumString实现对象,但rgelt参数明确定义为[out]参数,因此它不必为其释放任何内存。只是这个“输出信息”在.NET端丢失了,所以当调用方出错时没有警告或错误。我想最好总是使用包装器进行COM互操作,即使它们看起来像“真正的”网络公民!
HRESULT Next(
  [in]   ULONG celt,
  [out]  LPOLESTR *rgelt,
  [out]  ULONG *pceltFetched
);
string[] item = new string[100]; // Create a new instance for each call.
item[0] = "some string value"; // THIS WILL CAUSE A LEAK
enumString.RemoteNext(item.Length, item, out fetched);
string[] item = new string[1];
for (; ; )
{
    int fetched;
    item[0] = null;
    enumString.RemoteNext(1, item, out fetched);
    if (fetched == 1)
    {
       // do something with item[0]
    }
    else
    {
        break;
    }
}