使用非托管 C# 和 C++ 时的内存 allocation/deallocation
Memory allocation/deallocation when working with C# and C++ unmanaged
我正在处理一些 C# 和 C++ 非托管代码,在处理内存时有两件事我不明白。如果有人可以帮助我理解:
如果一个变量在C#下动态分配(使用new)然后传递给C++非托管代码。用户是否需要在 C++ 非托管代码下手动释放该变量内存?
如果一个变量在 C++ 非托管下动态分配(使用 new),然后传递给 C#,那么垃圾收集器将释放该内存是否安全?
根据经验,component
/object
分配内存的那个应该释放内存。对于每个 new
delete
由 new
.
这就是理想。如果由于诸如您 C++
程序可能终止并且在分配内存的生命周期结束时不存在等原因而未遵循,则您的 C#
应该清理,反之亦然。
真的很简单!
- 取决于
- 取决于
呃,抱歉。
- 在典型情况下,C# 会跟踪内存并在不再使用后随时删除它在 C# 端。它无法在 C++ 端跟踪引用,因此互操作中的一个常见错误是内存在非托管端完成之前被释放 (导致 FUN 负载)。这仅适用于直接引用内存的情况,而不适用于复制内存的情况(典型情况是
byte[]
在非托管调用期间固定)。当传递给非托管代码的 object/pointer 的生命周期应该长于调用方法的 运行 时,不要使用自动编组。
- 在典型情况下,C# 无法跟踪 C++ 代码中的内存分配,因此您不能依赖自动内存管理。也有例外(例如某些 COM 场景),但您几乎总是需要手动管理内存。这通常意味着将指针发送回 C++ 代码以进行释放,除非它使用某种全局分配器(例如 CoMemoryInitialize)。请记住,在非托管世界中,没有一个内存管理器可以安全地调用来处理内存;反正你真的没有必要的信息。
当然,这只适用于指针。传递整数是完全没问题的,使用自动编组通常意味着编组器会处理大部分细节(尽管仍然只是在最简单的情况下,所以要小心)。非托管代码是 unmanaged - 您需要完全理解内存是如何分配的,以及如何、何时以及由谁负责清理内存。
不,因为对象是在托管堆上分配的,GC 将照常处理释放。问题是你必须告诉他在从非托管代码使用对象时不要释放或更改对象的地址,因为 GC 不知道你要从非托管代码使用对象多长时间。这可以通过 PINNING 对象来完成。
查看 this 问题的答案。
不,因为对象是在 C++ 非托管堆上分配的,GC 不会触及它。您必须使用 delete 自行解除分配。
编辑:
如果您需要在托管代码中分配一个对象并在非托管代码中取消分配,反之亦然,很高兴知道有 OS 堆用于此目的,您可以通过 C# 的 Marshal.AllocHGlobal and Marshal.FreeHGlobal 调用使用它,将在 C++ 中是类似的调用。
我正在处理一些 C# 和 C++ 非托管代码,在处理内存时有两件事我不明白。如果有人可以帮助我理解:
如果一个变量在C#下动态分配(使用new)然后传递给C++非托管代码。用户是否需要在 C++ 非托管代码下手动释放该变量内存?
如果一个变量在 C++ 非托管下动态分配(使用 new),然后传递给 C#,那么垃圾收集器将释放该内存是否安全?
根据经验,component
/object
分配内存的那个应该释放内存。对于每个 new
delete
由 new
.
这就是理想。如果由于诸如您 C++
程序可能终止并且在分配内存的生命周期结束时不存在等原因而未遵循,则您的 C#
应该清理,反之亦然。
真的很简单!
- 取决于
- 取决于
呃,抱歉。
- 在典型情况下,C# 会跟踪内存并在不再使用后随时删除它在 C# 端。它无法在 C++ 端跟踪引用,因此互操作中的一个常见错误是内存在非托管端完成之前被释放 (导致 FUN 负载)。这仅适用于直接引用内存的情况,而不适用于复制内存的情况(典型情况是
byte[]
在非托管调用期间固定)。当传递给非托管代码的 object/pointer 的生命周期应该长于调用方法的 运行 时,不要使用自动编组。 - 在典型情况下,C# 无法跟踪 C++ 代码中的内存分配,因此您不能依赖自动内存管理。也有例外(例如某些 COM 场景),但您几乎总是需要手动管理内存。这通常意味着将指针发送回 C++ 代码以进行释放,除非它使用某种全局分配器(例如 CoMemoryInitialize)。请记住,在非托管世界中,没有一个内存管理器可以安全地调用来处理内存;反正你真的没有必要的信息。
当然,这只适用于指针。传递整数是完全没问题的,使用自动编组通常意味着编组器会处理大部分细节(尽管仍然只是在最简单的情况下,所以要小心)。非托管代码是 unmanaged - 您需要完全理解内存是如何分配的,以及如何、何时以及由谁负责清理内存。
不,因为对象是在托管堆上分配的,GC 将照常处理释放。问题是你必须告诉他在从非托管代码使用对象时不要释放或更改对象的地址,因为 GC 不知道你要从非托管代码使用对象多长时间。这可以通过 PINNING 对象来完成。 查看 this 问题的答案。
不,因为对象是在 C++ 非托管堆上分配的,GC 不会触及它。您必须使用 delete 自行解除分配。
编辑: 如果您需要在托管代码中分配一个对象并在非托管代码中取消分配,反之亦然,很高兴知道有 OS 堆用于此目的,您可以通过 C# 的 Marshal.AllocHGlobal and Marshal.FreeHGlobal 调用使用它,将在 C++ 中是类似的调用。