Go中的终结器测试

Go中的终结器测试,go,garbage-collection,finalizer,canonicalization,Go,Garbage Collection,Finalizer,Canonicalization,TLDR:有没有办法合理地编写测试用例来测试终结器行为 我试图在Go中实现一个内存敏感的规范化映射/缓存。因为没有“软引用”的概念(而且因为我的对象图总是会形成一个DAG),所以我使用一个跟踪用户区中引用计数的微型接口/框架来实现这一点: type ReferenceCounted interface { RefCount() int IncRef() DecRef() (bool, error) } type Finalizable interface { Refer

TLDR:有没有办法合理地编写测试用例来测试终结器行为

我试图在Go中实现一个内存敏感的规范化映射/缓存。因为没有“软引用”的概念(而且因为我的对象图总是会形成一个DAG),所以我使用一个跟踪用户区中引用计数的微型接口/框架来实现这一点:

type ReferenceCounted interface {
   RefCount() int
   IncRef()
   DecRef() (bool, error)
}

type Finalizable interface {
   ReferenceCounted
   Finalize()
}

type AbstractCounted struct {
     // unexported fields
}
其工作方式是,您有一个嵌入AbstractCounted的结构,并实现
Finalizable.Finalization()
——这些共同使该结构接收
Finalizable
接口。还有一个函数
func MakeRef(obj Finalizable)*Reference
,它返回一个指向一个结构的指针,该结构接收一个方法
func Get()Finalizable
,该方法获取引用目标,并通过增加非递归对象上的ref计数,然后设置一个终结器(通过
runtime.SetFinalizer()初始化)
),这将减少引用
AbstractCounted
Finalizable
实现依次调用结构上的
Finalize()
,当引用计数为零时,该结构将嵌入它

因此,一切都被设置为与Java中的软引用/引用队列非常相似的工作方式,唯一的例外是它是引用计数,而不是在活动词法作用域中寻找“软”可达的对象的标记/扫描

它似乎工作得很好!但是-我想写一个测试用例

我完全理解finalizer调用被延迟,并且不保证按照
reflect
包文档运行它们。在使用运行时gc和终结器(C#、VB、Java、Python等)的其他语言中,情况也是如此

但是,在所有其他语言中,请求显式GC(这里通过
runtime.GC()
函数)似乎确实会导致终结器运行。由于Go中不是这种情况,我无法找到一种编写触发终结器的测试用例的方法


是否有任何技巧或代码片段(我同意这取决于当前的实现,即将来可能会中断!)可以可靠地触发这些终结器,以便我可以编写测试?

您不能显式触发终结器,因此您能管理的最好方法是确保GC以
runtime.GC()
启动,并等待终结器运行

如果查看
运行时/mfinal_test.go
,有些测试会通过通道等待终结器调用:

runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
runtime.GC()
select {
case <-fin:
case <-time.After(4 * time.Second):
    t.Errorf("finalizer of next string in memory didn't run")
}

runtime.SetFinalizer(y,func(z*objtype){fin我想你说的是使用runtime.SetFinalizer?我想你需要展示一些实现。你是如何在数据结构中保存引用的,因为如果有任何活动指针,它们将不会被GC'处理,终结器也不会运行。@JimB-是的,runtime.SetFinalizer(),上面编辑过。一般来说,
runtime.GC
最终会触发终结器。如果你看一下
runtime/mfinal\u test.go
,你会看到一些简单的测试,在调用
runtime.GC
后,它们会等待通过通道调用终结器。我记得在某个地方读到,终结器很有可能被转储Go 1之后,仅供参考。@thwd:Go 1之后将是Go 2,这将是一种不同的语言,没有计划的发布日期,所以我认为我们现在不需要担心这个问题。在这里等待是我没有做的事情!测试现在很好,谢谢。(与Java eg相比,当gc请求调用返回时,gc请求可能会启动,但不会完成)