好的,所以这是罪魁祸首方法:
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",虽然我还不确定这次出了什么问题。
答案 0 :(得分:1)
我认为问题是C函数正在分配对象,但D没有保留引用。如果在严格的内存环境中背靠背调用FunctionDecl_new
,那么将会发生以下情况:
段错误并不总是运行,因为如果有内存可供使用,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
。这将导致存储时最不令人惊讶的摩擦。
如果暂时使用该对象,可以将普通指针传递给它,因为接收函数不会存储或释放临时用法,因此存在较少的危险。在调用堆栈中,您的引用仍然存在,如果没有别的话。