c# – CC++LI库中的早期完成和内存泄漏

c# – CC++LI库中的早期完成和内存泄漏,第1张

概述我遇到的问题似乎是在我正在研究的C/C++LI(和C#)项目的早期调用终结器.这似乎是一个非常复杂的问题,我将从代码中提到很多不同的类和类型.幸运的是它是开源的,你可以在这里继续: Pstsdk.Net(mercurial repository)我也尝试在适当的时候直接链接到文件浏览器,这样你就可以在阅读时查看代码.我们处理的大多数代码都在存储库的pstsdk.mcpp文件夹中. 现在的代码处于相 我遇到的问题似乎是在我正在研究的C/C++li(和C#)项目的早期调用终结器.这似乎是一个非常复杂的问题,我将从代码中提到很多不同的类和类型.幸运的是它是开源的,你可以在这里继续: Pstsdk.Net(mercurial repository)我也尝试在适当的时候直接链接到文件浏览器,这样你就可以在阅读时查看代码.我们处理的大多数代码都在存储库的pstsdk.mcpp文件夹中.

现在的代码处于相当可怕的状态(我正在研究它),我正在处理的代码的当前版本是在Finalization修复程序(UNStable!)分支中.该分支中有两个变更集,为了理解我的冗长问题,我们需要同时处理这两个变更集. (变更集:ee6a002df36f和a12e9f5ea9fe)

对于某些背景,此项目是用C编写的unmanaged library的C/C++li包装器.我不是该项目的协调员,有几个我不同意的设计决策,因为我相信很多看过这些代码的人会,但我离题了.我们在C/C++li dll中包含了大部分原始库层,但是在C#dll中展示了易于使用的API.这样做是因为项目的目的是将整个库转换为托管C#代码.

如果您能够获取要编译的代码,则可以使用this test code重现该问题.

问题

名为moved resource management code to finalizers,to show bug的最新变更集显示了我遇到的原始问题.此代码中的每个类都使用相同的模式来释放非托管资源.这是一个例子(C/C++li):

DBContext::~DBContext(){    this->!DBContext();    GC::SuppressFinalize(this);}DBContext::!DBContext(){    if(_pst.get() != nullptr)        _pst.reset();            // _pst is a clr_scoped_ptr (managed type)                                 // that wraps a shared_ptr<T>.}

此代码有两个好处.首先,当像这样的类在using语句中时,资源会立即正确释放.其次,如果用户忘记了处理,当GC最终决定完成该类时,将释放非托管资源.

这是这种方法的问题,我根本无法理解,有时,GC会决定最终确定一些用于枚举文件中数据的类.许多不同的PST文件都会发生这种情况,并且我已经能够确定它与被调用的Finalize方法有关,即使该类仍在使用中.

我可以在this file (download)1中始终如一地实现它.早期调用的终结器位于DBAccessor.cpp文件中的NodeIDCollection类中.如果您能够运行上面链接的代码(由于对boost库的依赖性,此项目很难设置),应用程序将因异常而失败,因为_nodes列表设置为null并且_db_由于终结器运行,指针被重置.

1)NodeIDCollection类中的枚举代码是否有任何明显的问题会导致GC在仍在使用时完成此类?

我只能通过下面描述的解决方法使代码正常运行.

难看的解决方法

现在,我能够通过将每个终结器(!classname)中的所有资源管理代码移动到析构函数(~classname)来解决此问题.这已经解决了这个问题,虽然它没有解决我为什么要尽早完成课程的好奇心.

但是,这种方法存在问题,我承认这对设计来说更是个问题.由于代码中指针的大量使用,几乎每个类都处理自己的资源,并且需要处理每个类.这使得使用枚举非常难看(C#):

foreach (var msg in pst.Messages)   {      // If this using statement were removed,we would have      // memory leaks      using (msg)        {             // code here      }   }

作用于集合中的项目的using语句对我来说是错误的,但是,使用这种方法非常有必要防止任何内存泄漏.没有它,即使调用了pst类上的dispose方法,也永远不会调用dispose并且永远不会释放内存.

我有意尝试改变这种设计.这个代码第一次编写时的根本问题,除了我对C/C++li几乎一无所知之外,我无法将本地类放在托管代码中.我觉得有可能使用范围指针,当类不再使用时会自动释放内存,但我不能确定这是否是一种有效的方法来解决这个问题,或者它是否可行.所以,我的第二个问题是:

2)以无痛方式处理托管类中非托管资源的最佳方法是什么?

详细说明,我可以用最近添加到代码中的clr_scoped_ptr包装器替换本机指针(clr_scoped_ptr.h来自this stackexchange问​​题).或者我是否需要将本机指针包装在scoped_ptr< T>之类的内容中?或者smart_ptr< T>?

感谢您阅读所有这些,我知道这很多.我希望我已经足够清楚,以便我可以从比我更有经验的人那里得到一些见解.这是一个很大的问题,我打算在它允许的时候添加赏金.希望有人可以提供帮助.

谢谢!

1此文件是免费提供的PST文件enron dataset的一部分

解决方法 clr_scoped_ptr是我的,来自 here.

如果有任何错误,请告诉我.

即使我的代码不完美,使用智能指针也是解决此问题的正确方法,即使在托管代码中也是如此.

您不需要(也不应该)在终结器中重置clr_scoped_ptr.每个clr_scoped_ptr本身都将由运行时完成.

使用智能指针时,您不需要编写自己的析构函数或终结器.编译器生成的析构函数将自动调用所有子对象上的析构函数,并且每个子对象终结器将在收集时运行.

仔细观察您的代码,NodeIDCollection确实存在错误. GetEnumerator()每次调用时都必须返回一个不同的枚举器对象,以便每个枚举都从序列的开头开始.您正在重复使用单个枚举器,这意味着在连续调用GetEnumerator()之间共享该位置.那很糟.

总结

以上是内存溢出为你收集整理的c# – C/C++LI库中的早期完成和内存泄漏全部内容,希望文章能够帮你解决c# – C/C++LI库中的早期完成和内存泄漏所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1228385.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-05
下一篇 2022-06-05

发表评论

登录后才能评论

评论列表(0条)

保存