问题描述
我在Google上进行了搜索,但找不到类似的内容,因此我认为应该在此处发布.
我有一个成熟的,高性能的本机库,其中进行了许多优化,包括使用区域分配器来提高性能.各种旧有软件都在使用它,并且/但是我正在构建一个托管包装器,以将该功能公开给(例如)C#.由于各种原因,包括性能,都不能将所有代码都移至托管代码.我必须在本机和托管对象之间建立循环引用:
*托管类需要一个指向本机类的用于业务逻辑的指针;包装程序只需要调用本机代码即可
*本机代码将本机对象存储在有序集合中,因此我保留了从本机类返回到托管类的迭代器等的引用.-我不想重复列表和集管理
示例说明代码:
Hi,
I did a google search and couldn''t find something quite like this, so I thought I''d post here.
I''ve got a mature, high performance native library with lots of optimizations including using zone allocators to increase performance. It''s used by a variety of legacy software, and/but I''m building a managed wrapper to expose that functionality to (e.g.) C#. Moving all the code to managed is not an option for a variety of reasons, including performance. I''ve had to build a circular reference between the native and managed objects:
* the managed class needs a pointer to the native class for business logic; the wrapper does nothing other than call to the native code
* the native code stores native objects in ordered sets, so I retain a reference from the native class back to the managed class for iterators, etc. - I don''t want to duplicate list and set management
Example illustrative code:
ref class Managed_eh {
class Native_eh *n_this; // this is always valid
};
class Native_eh { // handled by a zone allocator
Native_eh *prev, *next; // doubly-linked list
msclr::auto_gcroot<managed_eh> ^gc_this; // lazy assignment
};</managed_eh>
*如果客户端代码创建了托管对象,则必须创建本机对象.
*如果本机对象是由于内部逻辑而创建的,那么只有在需要-避免不必要的分配等之前,我们才创建托管包装器,否则将失去区域分配器等的好处.同样,在很多时候,实际上不需要托管包装器.
* gc_this
不得清除(以保持一致性),只要存在带有Managed_eh
对象句柄的客户端托管代码即可.
清理是个问题.包装类和本机类都不知道什么时候它们不再对客户端代码和清理有用.有时我可以通过清理管理Native_eh
集的容器对象来解决此问题.但这不是通用解决方案.
*在严格的原生世界中,通过更改层次结构,这将是微不足道的解决方案.
*在COM参考计数器世界中,使用其他解决方案同样容易:
—如果未设置gc_this
,则当Managed_eh的引用计数器达到0
时进行清理 —如果设置了gc_this
,则当托管指针上的引用计数器降为1时,我们知道可以清除,因为我们确切知道"1"是什么(gc_this
).
解决此问题的合适的".Net"方法是什么?
谢谢,
—Rob
有趣的讨论,谢谢,尽管我认为这没有帮助解决我的特定问题.
我并不特别关注何时清理垃圾收集的对象,我可以轻松地解决该问题.我更担心他们可以.
*只要本机对象保留托管包装器的句柄,垃圾收集器就不会执行清理.测试表明了这一点,这是有道理的,因为垃圾收集器无法在本机端跟踪引用.
*但是...本机对象无法释放其句柄,直到知道被管理方没有其他人拥有句柄-否则就有使一致性失效的风险.
换句话说,如果本机对象具有对托管包装器的唯一引用,则我们(它)可以执行清理.但是本机对象如何确定没有其他人对其包装感兴趣?
在COM世界中,我们可以检查参考计数器得出的结论是:"我"是唯一拥有该对象句柄的对象,因此我可以释放它并清理".但是我看不到.Net世界中可用的内容.
有没有办法(有效地)查询垃圾收集器使用的依赖关系图?
是否有事件告诉您给定对象(托管包装器)的依赖关系图的状态何时更改?
—Rob
* If client code creates the managed object, then the native object is necessarily created.
* If the native object is created due to internal logic, then we don''t create the managed wrapper until we need to - to avoid unnecessary allocations, etc., otherwise the benefits of the zone allocators, etc. are lost. Also, there are many times when the managed wrapper is never actually needed.
* gc_this
must not be cleared (to maintain consistency) so long as there''s client managed code with a handle to the Managed_eh
object.
Clean-up is the concern. Neither the wrapper class, nor the native class know when they are no longer interesting to client code and clean up. Sometimes I can solve this problem by cleaning up the container objects managing the sets of Native_eh
. But it''s not a generic solution.
* In the strictly native world, this would be a trivial fix by changing the hierarchy.
* In the COM reference-counter world, this is equally easy with a different solution:
— if gc_this
is not set, then clean up when the reference counter to Managed_eh reaches 0
— if gc_this
is set, then when the reference counter on the managed pointer drops to 1, we know we can clean up because we know exactly what that ''1'' is (gc_this
).
What is the appropriate ".Net" approach to solving this problem?
Thanks,
—Rob
Interesting discussion, thanks, though I don''t think it helps my particular problem.
I''m not particularly concerned on precisely when the clean-up of the garbage-collected objects occur, I can easily work around that. I''m more concerned that they can.
* So long as the native object retains the handle to the managed wrapper, then the garbage collector will not perform clean-up. Tests show this, and it makes sense because the garbage collector cannot track references in the native side.
* But...the native object cannot release its handle until it knows that nobody else on the managed side has a handle - or risk invalidating consistency.
Said another way, if the native object has the only reference to the managed wrapper, than we (it) can perform clean-up. But how can the native object determine that nobody else is interested in its wrapper?
In the COM world, we could examine reference counters to conclude "I''m the only one left with a handle to this object so I can release it and we can clean up". But I don''t see that available in the .Net world.
Is there a way to (efficiently) query the dependency graph used by the garbage collector?
Is there an event that tells you when the state of the dependency graph for a given object (the managed wrapper) changes?
—Rob
推荐答案
这篇关于托管/本机循环引用清理问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!