gpt4 book ai didi

c# - C++/CLI库中的早期完成和内存泄漏

转载 作者:行者123 更新时间:2023-11-30 12:51:04 26 4
gpt4 key购买 nike

我遇到的终结器问题似乎在我正在研究的C ++ / CLI(和C#)项目的早期。这似乎是一个非常复杂的问题,我将在代码中提及很多不同的类和类型。幸运的是,它是开源的,您可以在此处进行以下操作:Pstsdk.Net(Mercurial存储库)我还尝试了在适当的地方直接链接到文件浏览器,以便您在阅读时可以查看代码。我们处理的大多数代码都在存储库的pstsdk.mcpp文件夹中。

现在的代码处于相当可怕的状态(我正在努力处理),而我正在处理的代码的当前版本在Finalization fixes (UNSTABLE!)分支中。该分支中有两个变更集,并且要理解我的难题,我们需要同时处理这两个问题。 (更改集:ee6a002df36fa12e9f5ea9fe

在某些背景下,该项目是用C ++编写的unmanaged library的C ++ / CLI包装。我不是项目的协调员,我有一些不同的设计决策,因为我敢肯定,许多关注代码的人都会做,但我离题。我们将原始库的许多层包装在C ++ / CLI dll中,但在C#dll中公开了易于使用的API。这样做是因为项目的目的是将整个库转换为托管C#代码。

如果能够编译代码,则可以使用this test code重现该问题。



问题

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

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 ++ / CLI几乎一无所知的事实之外,首次编写此代码时的根本问题是我无法将本机类放入托管类中。我觉得有可能使用范围更大的指针,当不再使用该类时,该指针将自动释放内存,但是我不确定这是否是解决此问题的有效方法,甚至无法奏效。所以,我的第二个问题是:

2)以一种轻松的方式处理托管类中的非托管资源的最佳方法是什么?

详细地说,我是否可以将本机指针替换为刚添加到代码中的 clr_scoped_ptr包装器(来自 clr_scoped_ptr.h stackexchange问​​题的 this)。还是我需要将本机指针包装在 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++/CLI库中的早期完成和内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7758879/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com