LevelDB 在删除 LevelDB 实例时断言

Posted

技术标签:

【中文标题】LevelDB 在删除 LevelDB 实例时断言【英文标题】:LevelDB asserts when deleting the LevelDB instance 【发布时间】:2011-08-17 23:10:44 【问题描述】:

当我尝试删除 leveldb 实例时,我得到了一些非常烦人的断言,但我不确定为什么会这样!

断言发生在version_set.cc 文件中:

void VersionSet::AppendVersion(Version* v) 
  // Make "v" current
  assert(v->refs_ == 0); // <---??? how do I avoid this assertion?
  // the rest of the source code is available in the link to version_set.cc

另外,它在同一个文件的另一个地方断言:

Version::~Version() 
  assert(refs_ == 0); // <-- Again... how do I avoid this one too?
  // the rest of the source code is available in the link to version_set.cc

这里有更多关于我系统使用的背景细节,我有一个:

ExtStorage(扩展存储),它有一个LevelDB::DB 实例。 EextStorageDotNet 类,它是 ExtStorage 周围的 C++/CLI 包装器。 class AltStorage,它持有一个指向 ExtStorage 类的指针(通过构造函数传递): AltStorageDotNet 类,它是 AltStorage 的 C++/CLI 包装器。

备用存储类如下所示:

class AltStorage
    ExtStorage* instance;
public:
    AltStorage(ExtStorage* extStorage):instance(extStorage)

    ~AltStorage()
        delete instance;
        instance = NULL;
    
;

ExtStorage 类如下所示:

class ExtStorage
    leveldb::DB* mydb;
public:
    ExtStorage(/*some parameters*/)
         mydb = new leveldb::DB(/*parameters*/);
    

    // Destructor
    ~ExtStorage() 
        Close();
    

    // deletes the leveldb::DB instance
    void Close() 
        if(mydb == NULL) 
            delete mydb; // <-- Asserts every time I get here when using with the AltStorageDotNet
            mydb= NULL;

            // Close the L1 and L2 caches
            // only once (
        
    

AltStorageDotNet 类如下所示:

public ref class AltStorageDotNet
    AltStorage* altInstance;
    ExtStorageDotNet^ extInstance;
public:
    AltStorageDotNet() 
        ExtStorage extStorage = new ExtStorage(/*params*/);
        altInstance = new AltStorage(extStorage);
        extInstance = gcnew ExtStorageDotNet(extStorage);
    

    ~AltStorageDotNet()
        delete altInstance;
        altInstance = NULL;
        // no need to delete extInstance since it was created with gcnew 
    

    !AltStorageDotNet()
        delete altInstance;
        altInstance = NULL;
        // no need to delete extInstance since it was created with gcnew
    

    inline ExtStorageDotNet^ GetExtInstance()return extInstance;
;

DotNet 包装器如下所示:

public ref class ExtStorageDotNet
private:
    ExtStorage* instance;
public:
    ExtStorageDotNet(ExtStorage* extStorage)
        instance = extStorage;
    

    ~ExtStorageDotNet()
        delete instance;
        instance = NULL;
    

    !ExtStorageDotNet()
        delete instance;
        instance = NULL;
    

    void Close()instance->Close();
;

每当我在我的 C# 应用程序中使用 ExtStorageDotNet 包装器时,一切正常,并且没有断言。但是,当我使用 AltStorageDotNet 包装器并访问 ExtStorageDotNet 包装器时,我会在关闭数据库时得到断言。 这是测试套件的所有部分,我在其中为每个测试用例初始化一个实例,并在每个测试用例后关闭它;在新的测试用例开始之前,相关的数据库文件会被删除。我看不出它应该发生的任何原因,并且断言对追踪问题没有帮助。

【问题讨论】:

// no need to delete extInstance since it was created with gcnew 此评论非常误导。 我无法删除 extInstance,没有 gcdelete... 我可以将其设置为 null,但仅此而已。 "gcnew 用于 .NET 引用对象;使用 gcnew 创建的对象会自动进行垃圾回收;将 gcnew 与 CLR 类型一起使用很重要" (***.com/questions/202459/what-is-gcnew/202464#202464) 不,没有gcdelete,但delete 在托管类型和非托管类型上的语义完全不同。您仍然应该使用它,并且在某些情况下需要——这很可能是其中一种情况。 自动垃圾回收呢? 垃圾收集是关于内存管理的; IDisposable 是关于确定性的最终确定——出于您的目的,这些是完全正交的概念。 【参考方案1】:

我摆脱了嵌套引用,但这并没有解决问题。事实证明,导致这个问题的问题并不完全是我所看到的。当用户获取数据库的迭代器并且在删除数据库之前未能删除迭代器时,就会出现此问题。这在google group relating to level db 中进行了讨论。

// Caller should delete the iterator when it is no longer needed.
// The returned iterator should be deleted before this db is deleted.
virtual Iterator* NewIterator(const ReadOptions& options) = 0; 

当用户获得迭代器时,他们应该在删除数据库之前将其删除,否则他们将获得上述断言。

【讨论】:

【参考方案2】:

我不知道它是否与您的断言有关,但此代码肯定会导致内存损坏。 ExtStorageDotNetAltStorageDotNet 都是一次性的和可终结的,并且两者(直接或间接)delete 都是 ExtStorage 的实例——总是,一个人会在另一个人已经拥有它之后delete

此外,正如我在评论中所说,您的代码中的注释no need to delete extInstance since it was created with gcnew 已经过时了——有关详细信息,请参阅this answer。 (我假设您了解 C#,因此了解 IDisposable;如果您不了解,那么您真的需要阅读。)

【讨论】:

好的,如果delete 等同于空指针上的nop(这是我在删除它后设置的指针),我看不出我会如何获得内存损坏。此外,根据the answer on this question,使用 gcnew 创建的对象会自动被垃圾收集。 @Lirik :您将ExtStorageDotNet::instance 设置为NULL,但您认为这会如何影响AltStorage::instance(反之亦然)?另外,请忘记本次讨论的垃圾收集,因为它与IDisposable 完全无关。 ;-] 知道了:不需要删除AltStorage::instance @Lirik:不完全是。 :-/ 您需要重组代码,以便只有一个对象拥有给定的非托管对象,并且您需要 delete 您在析构函数中拥有的托管对象(但不是终结器)。再次,请阅读有关IDisposable 的信息; MSDN上有很多关于正确实现它的文章。 好的,我没有实现IDisposable,那么删除AltStoragDotNet::extInstance对我有什么帮助?

以上是关于LevelDB 在删除 LevelDB 实例时断言的主要内容,如果未能解决你的问题,请参考以下文章

leveldb学习笔记

LevelDb简单介绍和原理——本质:类似nedb,插入数据文件不断增长(快照),再通过删除老数据做更新

Leveldb数据Compaction源码分析

LevelDB 源码剖析:环境搭建实战使用常用优化

再学LevelDB Compaction

再学LevelDB Compaction