In my project, at game start, I pass a MonoObject* to native code, which is cached for later use. However, I found that the C# object might be moved by the garbage collector, causing the MonoObject* to become invalid. While it seems that the object can be pinned using mono_gchandle_new(object, true), this approach appears to have a performance impact, see the following comment:
mono_gchandle_new() is used to prevent an object from being garbage collected until mono_gchandle_free() is called. Use a TRUE value for the pinned argument to prevent the object from being moved (this should be avoided as much as possible and should only be used for short periods of time to avoid performance degradation).
I also discovered the RequiredByNativeCodeAttribute, and I’m curious if Unity uses some method to call C# code that involves pinning the object or using another approach to retrieve the object’s address in the managed world for the call.
I can’t speak to the RequiredByNativeCodeAttribute stuff, but I will observe that the intended way for managed → unmanaged interop is to pin stuff for the shortest time possible, then unpin it.
You could also cache the values within the object by scraping a copy of it out on the C++ side, then only sending updates when those things change.
It would really depend on how dynamic the info in this object is, how quickly it would fall out of date.
Thanks for your reply. The MonoObject* I want to use is not temporary—it needs to persist throughout the program’s lifespan. So, briefly pinning and unpinning it won’t work.
The example with RequiredByNativeCodeAttribute is uncommon, and I found a more typical case used by Unity. For custom MonoBehaviour instances (components attached to GameObjects in the scene), each has a C# object bound to a C++ object, and both persist for the program’s lifespan. Every frame, Unity calls Update on the C# side from its C++ side.
I’d like to understand how Unity manages C# object addresses from C++, given that the GC can move C# objects.
As I recall, part of the reason Unity uses such an outdated Garbage Collector in the first place is because of this issue. Apparently, using a generation system is extremely difficult to work with int his case. I vaguely recall that it might even be specifically because they pin the memory.
Thanks for your reply. If Unity pins the memory for MonoBehaviour instances, then I think it’s fine to pin my C# objects as well, especially since I only need to pin a few, while Unity already pins many C# objects.
However, I will find time to test this behavior in a long-running program with continuously new objects to trigger GC and check whether the addresses of MonoBehaviour instances remain fixed.