我正在为一个玩具程序语言开发一个爱好编译器/解释器,我已经实现了我开始探索的大部分功能,除了一个好的垃圾收集算法(类似于this guy)。我已经阅读了很多关于各种算法的内容,我对如何实现它们有一个大概的了解。我的语言运行时的早期迭代使用了引用计数,但我放弃它以学习更高级的东西,所以我现在正在考虑标记和复制压缩算法。
我开始的第一个问题是阻止算法在本机扩展函数中收集“对象”(即用C编写的函数)。根集由解释器堆栈中的“对象”和符号表中的“对象”组成,我不应该对它们有太多麻烦,但是,如果在C函数中创建容器“对象”,则填充子'对象',如何防止GC收集它们,因为它实际上不在解释器堆栈上或绑定到符号?
使实施GC更容易的事情:
用户代码:
f = open('words.txt', 'r');
lines = readlines(f);
close(f);
解释器(解析后,编译为字节码...):
push
filename,open_mode builtin_fopen
,返回包含FILE*
f
f
builtin_flines
创建列表类型l
,然后使用C fread
读取每一行
将该文件作为字符串类型,将其附加到列表l
lines
中,依此类推.... 现在,如果GC在分配文件中包含一行的其中一个字符串时运行,则根集合尚未引用l
,因此应该收集它。
关于如何更好地处理这个问题的任何想法?
答案 0 :(得分:3)
答案 1 :(得分:1)
您需要为您的本机函数提供一个接口,通过该接口,它们可以告诉垃圾收集器它们引用了哪些对象,然后让它们使用该接口。
最简单的方法可能是不让本机代码直接指向解释器/垃圾收集数据。相反,您为本机代码提供对象的句柄,并让它回调到运行时以从对象获取值。在您的示例中,builtin_flines
将调用运行时来分配列表并返回它的句柄。然后它会读取行,并调用运行时将每个行追加到列表中,最后返回完整列表。运行时将管理给定本机调用的所有句柄,在本机调用返回后释放它们。
答案 2 :(得分:1)
因为我是你提到的最初的“这个”家伙,虽然我可以根据我迄今为止在我的项目中设计的内容给你一些关于你的第一个问题的见解(我保证最终会在博客上发表)。首先,所有内存分配都通过mutator函数。输入参数是您正在创建的对象的类型,以及对指向它的指针类型对象的引用。然后在创建新对象时更新该指针对象。如果正在为解释器运行时中的C函数专用分配对象,则它是根对象。在这种情况下,NULL作为第二个参数传递,并且该对象将添加到根对象列表中。现在稍后,如果该内部函数不再需要该对象,则必须从根对象列表中删除该对象。 (它不会取消分配对象本身,因为最终将由垃圾收集例程处理)。哦,解释器堆栈本身也是解释器中的一个对象(列表类型或数组类型对象),因此指向它的指针也位于根对象列表中(同样,另一个列表类型对象也是解释者知道的。指向根对象列表的指针是垃圾收集器需要知道的唯一指针。
此外,至于何时开始垃圾收集运行 - 由于内存在现代架构中实际上是无限制的,所以我决定在分配X个对象时启动垃圾收集器。跑完后你剩下Y对象。如果Y仍然大于X的Z%,那么X会被提升到足够的程度。然后我只希望malloc()永远不会失败(如果确实如此,我只是抛出错误并退出解释器)。
希望这会有所帮助,希望其他人会增加更多的说明,因为在语言/口译设计方面我更喜欢业余爱好者。
答案 3 :(得分:0)
一些并发症:
输入要解释的行时 100如果X然后gosub 5000
但5000还不存在,你是意大利面编码...... 也许x还没有任何指定的值或数据类型。 如果我们现在没有索引,我们是否要等到有人 类型“运行”或直接从提示执行一行?
如果我们现在做索引以便以后加快速度,那么我们将如何呢? 知道“100”或“X”或“5000”的最后一个实例 去掉?
我们在“事物”的主索引中做了什么条目? 假设这些东西可能包括基本代码行, 字符串,以及我们想要按名称处理的其他变量 或行号。
我们希望快速找到,并用于战略性识别 垃圾收集潜力何时需要收集 就产生了。
我们在事物索引上燃烧了多少静态空间 可能会改变大小?哪个细节除了标签, 位置和长度足以证明索引的合理性? 我们是否应该尝试在变量时索引空白空间 收缩?或者只是索引变量的最大历史记录 尺寸及其当前尺寸?我们如何识别这些 最常变化的变量,应该是 我们避免清洗它们,甚至故意填充它们?
我们什么时候清理整个烂摊子?或者它更好 碎片整理只有足够的自由空间来挤压一些东西 不能以其他方式卡在一个现有的洞里?
有目的的延迟和等待“输入”似乎是很好的目标 我们可能会利用它来主动清理一些 一塌糊涂。无法保证任何基本程序都会有这样的 死区时间。
对不起,这不是答案,但原来的问题似乎 邀请一些头脑风暴来寻求更好的计划。我们需要 一个明确的战略,需要定义整个问题。