为什么我的跟踪参考不更新?

时间:2012-02-16 16:26:16

标签: .net garbage-collection c++-cli tracking-reference

我创建了一个简洁的小函数来删除一个不可数及其内容(插入我最近发现的内存泄漏):

generic<typename T>
void CollectionHelpers::DeleteEnumerable(IEnumerable<T>^% enumerable)
{
      if(enumerable != nullptr)
      {
            for each( T obj in enumerable)
            {
                  delete obj;
            }

            delete enumerable;
            enumerable = nullptr;
      }
}

...但由于某些原因,当我使用调试器进行跟踪时,当我从DeleteEnumerable函数返回时,列表仍然显示为某些内存。

我认为通过传入作为跟踪引用它应该修改我传入的句柄?我在这里错过了什么?


修改:更全面的测试示例......

这是一个稍微更新的处理器:

using namespace GenericCollections;
using namespace System::Collections::Generic;
using namespace System;

generic<typename T>
void CollectionHelpers::DeleteEnumerable(IEnumerable<T>^% enumerable)
{
    if(enumerable != nullptr)
    {
        for each( T obj in enumerable )
        {
            Console::WriteLine("Disposing of object");
            delete obj;
        }

        Console::WriteLine("Disposing of enumerable");
        delete enumerable;
        enumerable = nullptr;
    }

    if( enumerable == nullptr )
    {
        Console::WriteLine("enumerable tracking reference is nullptr");
    }
    else
    {
        Console::WriteLine("enumerable tracking reference is NOT nullptr");
    }
}

这是调用处理程序的代码的缩减示例:

using namespace System;
using namespace GenericCollections;
using namespace System::Collections::Generic;

ref class MyClass
{
private:
    int* m_myBuf;

public:
    MyClass()
    {
        m_myBuf = new int[100];
        Console::WriteLine("MyClass::MyClass()");
    }

    ~MyClass()
    {
        delete [] m_myBuf;
        m_myBuf = NULL;
        Console::WriteLine("MyClass::~MyClass()");
    }
};

int main(array<System::String ^> ^args)
{

    List<MyClass^>^ myList = gcnew List<MyClass^>;

    myList->Add(gcnew MyClass());
    myList->Add(gcnew MyClass());
    myList->Add(gcnew MyClass());

    CollectionHelpers::DeleteEnumerable(myList);

    if(myList == nullptr)
    {
        Console::WriteLine("Original list is disposed of");
    }
    else
    {
        Console::WriteLine(String::Format("Original list still referenced: {0}", myList));
    }

    return 0;
}

......这是输出:

MyClass::MyClass()
MyClass::MyClass()
MyClass::MyClass()
Disposing of object
MyClass::~MyClass()
Disposing of object
MyClass::~MyClass()
Disposing of object
MyClass::~MyClass()
Disposing of enumerable
enumerable tracking reference is nullptr
Original list still referenced: System.Collections.Generic.List`1[MyClass]

老实说,我并不担心删除可枚举的内容是否是包含在其中的对象需要被删除。我们在整个地方使用这些列表,而且我们的系统内存不足,因为它们没有得到足够快的清理。

更糟糕的是,我们依靠DeleteEnumerable函数将列表跟踪引用设置为nullptr,但是当它们被传回时,跟踪引用似乎没有更新。从控制台输出中可以看出,这不仅仅是调试器问题。

我不明白为什么

1 个答案:

答案 0 :(得分:2)

好的,这种行为确实很奇怪。这就是实际发生的事情:

您有一个List<MyClass^>^类型的局部变量,并且您正在调用类型为IEnumerable<T>^%的参数的方法。这是个问题。该方法可以将其参数设置为array<T>^。在C#中,不允许这样的代码,但出于某种原因,它在C ++ / CLI中是允许的。它编译成如下:

List<MyClass^>^ myList = …;
IEnumerable<MyClass^>^ enumerable = myList;
CollectionHelpers::DeleteEnumerable(enumerable);

你看到了问题吗?如果您在nullptr中将引用设置为DeleteEnumerable(),则局部变量enumerable会更改,但不会更改myList

要验证,请将您的代码更改为:

IEnumerable<MyClass^>^ myEnumerable = myList;

CollectionHelpers::DeleteEnumerable(myEnumerable);

if(myEnumerable == nullptr)
{
    Console::WriteLine("Original list is disposed of");
}
else
{
    Console::WriteLine(String::Format("Original list still referenced: {0}", myList));
}

并正确打印“原始列表已被处理掉”。

如果你想知道为什么 C ++ / CLI会以这种方式运行,那么跟踪引用的行为是否与普通的C ++引用相似?