以下是C++CLI代码被编译成DLL,并被C#应用程序调用。
void Foo(String^ strManaged) {
marshal_context^ context = gcnew marshal_context();
FooUnmanaged(context->marshal_as<const char*>(strManaged));
}
FooUnmanaged()
读取 const char*
,运行一些处理,大约需要一秒钟,然后读取 const char*
再比如,。
void FooUnmanaged(const char* str) {
// 1
Log(str);
// Process things unrelated to 'str'
// ...
// 2
Log(str);
}
在某些情况下... str
读取的第一和第二之间的变化 FooUnmanaged()
就像该内存被重新用于其他用途一样。无论在 FooUnmanaged()
只要它需要一个明显的时间量(我想,足够长的时间让GC有机会触发)。
这种情况不会发生,如果 Foo
要么这样写
void Foo(String^ strManaged) {
marshal_context^ context = gcnew marshal_context();
FooUnmanaged(context->marshal_as<const char*>(strManaged));
delete context; // addded
}
或者那样
void Foo(String^ strManaged) {
marshal_context context; // created on the stack
FooUnmanaged(context.marshal_as<const char*>(strManaged));
}
原代码是否有误?为什么它不能正确地预留出内存的 const char*
终身 context
? 或者说,可以用一生的时间 context
比我想象中的要短(的范围)。Foo()
)?
由 @HansPassant 提供的答案。
是的,这是一个终身问题。.NET使用了一个激进的收集器,它不知道本地代码依赖于上下文。第一个片段需要
GC::KeepAlive(context);
在最后。最后一个片段是如何使用的,堆栈语义效仿RAII,自动生成的Dispose()
调用以类似的方式保持其活力。并且避免了临时的内存泄漏。如果FooUnmanaged()
存储传递的指针,那么你就不能使用marshal_context
.
这一点得到了证实。本文:
局部变量】的寿命可能取决于程序的构建方式。在调试构建中,只要方法在堆栈中,局部变量就会持续存在。在发行版构建中,JIT能够查看程序结构,计算出变量在执行过程中可以被方法使用的最后一点,并在不再需要它时将其丢弃。