垃圾收集的另一个奇怪问题是什么?

时间:2014-04-28 07:30:16

标签: garbage-collection segmentation-fault d dmd

好的,所以这是罪魁祸首方法:

class FunctionDecl
{
    // More code...

    override void execute()
    {
        //...
        writeln("Before setting... " ~ name);
        Glob.functions.set(name,this);
        writeln("After setting." ~ name);
        //...
    }
}

现在发生了什么:

  • 如果省略writeln("After setting." ~ name);行,则程序崩溃,此时
  • 如果我保留它(使用name属性是关键,而不是writeln本身),它可以正常工作。

那么,我想this会自动被垃圾收集?这是为什么? (指向与GC和D相关的一些可读参考的指针会非常棒)

我该如何解决?


更新:

刚刚在代码的最开头尝试了GC.disable()。并且......自动化,一切都恢复正常!所以,这就是我所怀疑的罪魁祸首。问题是:如何解决 out 完全消除垃圾收集?


更新II:

这里 functionDecl.d的完整代码 - "不必要的"代码省略:

//================================================
// Imports
//================================================

// ...

//================================================
// C Interface for Bison
//================================================

extern (C) 
{
    void* FunctionDecl_new(char* n, Expressions i, Statements s) { return cast(void*)(new FunctionDecl(to!string(n),i,s)); }
    void* FunctionDecl_newFromReference(char* n, Expressions i, Expression r) { return cast(void*)(new FunctionDecl(to!string(n),i,r)); }
}

//================================================
// Functions
//================================================

class FunctionDecl : Statement
{
    // .. class variables ..

    this(string n, Expressions i, Statements s)
    {
        this(n, new Identifiers(i), s);
    }

    this(string n, Expressions i, Expression r)
    {
        this(n, new Identifiers(i), r);
    }

    this(string n, Identifiers i, Statements s)
    {
        // .. implementation ..
    }

    this(string n, Identifiers i, Expression r)
    {
        // .. implementation ..
    }

        // .. other unrelated methods ..

    override void execute()
    {
        if (Glob.currentModule !is null) parentModule = Glob.currentModule.name;

        Glob.functions.set(name,this);
    }
}

现在Glob.functions.set(name,this);做了什么:

  • Glob是一个包含全局定义的实例
  • function是处理已定义函数的类实例(它带有FunctionDecl[] list
  • set只是这样做:list ~= func;

PS 我99%确定它与这个问题有关:Super-weird issue triggering "Segmentation Fault",虽然我还不确定这次出了什么问题。

1 个答案:

答案 0 :(得分:1)

我认为问题是C函数正在分配对象,但D没有保留引用。如果在严格的内存环境中背靠背调用FunctionDecl_new,那么将会发生以下情况:

  1. 第一个调用,创建一个新对象。那个指针进入C的土地,D GC无法看到它。
  2. 第二个是,分配另一个新对象。由于内存紧张(就GC池而言),它会尝试运行收集周期。它从(1)中找到对象,但找不到它的任何实时指针,所以它释放了它。
  3. C函数使用该释放的对象,导致段错误。
  4. 段错误并不总是运行,因为如果有内存可供使用,GC在分配第二个时不会释放该对象,它只会使用其空闲内存而不是收集。这就是为什么省略writeln可以摆脱崩溃:~运算符分配,这可能只是让你超过该内存行的边缘,触发一个集合(当然,运行{ {1}}让gc有机会在第一时间运行。如果你从不GC分配,你也不会GC收集 - 函数看起来有点像~

    有三种解决方案:

    • 在返回之前,立即在D结构的gc_allocate() { if(memory_low) gc_collect(); return GC_malloc(...); }函数中存储引用:

      FunctionDecl_new
    • 在将指针返回到C之前调用指针上的FunctionDecl[] fdReferences; void* FunctionDecl_new(...) { auto n = new FunctionDecl(...); fdReferences ~= n; // keep the reference for later so the GC can see it return cast(void*) n; } 。(我不喜欢这个解决方案,我认为数组更好,更简单。)

    • 使用GC.addRoot创建要提供给C的对象:

      malloc

    然后,当然,当你知道它不再被使用时,你应该void* FunctionDecl_new(...) { import std.conv : emplace; import core.stdc.stdlib : malloc; enum size = __traits(classInstanceSize, FunctionDecl); auto memory = malloc(size)[0 .. size]; // need to slice so we know the size auto ref = emplace!FunctionDecl(memory, /* args to ctor */); // create the object in the malloc'd block return memory.ptr; // give the pointer to C } 指针,但如果不这样做,那就不是真的错了。


    我遵循的一般规则btw是跨越语言障碍进行存储的任何内存(用法不同)应该与语言所期望的类似地进行分配:因此,如果将数据传递给C或C ++,则以C方式分配它,例如与free。这将导致存储时最不令人惊讶的摩擦。

    如果暂时使用该对象,可以将普通指针传递给它,因为接收函数不会存储或释放临时用法,因此存在较少的危险。在调用堆栈中,您的引用仍然存在,如果没有别的话。