TLDR:有什么方法可以合理地编写测试用例来测试终结器行为吗?
我正在尝试在 Go 中实现内存敏感的规范化映射/缓存。由于没有“软引用”的概念(并且因为我的对象图将始终形成一个 DAG),我使用一个微型界面/框架来跟踪用户空间中的引用计数:
type ReferenceCounted interface {
RefCount() int
IncRef()
DecRef() (bool, error)
}
type Finalizable interface {
ReferenceCounted
Finalize()
}
type AbstractCounted struct {
// unexported fields
}
它的工作方式是你有一个嵌入 AbstractCounted 的结构,并实现 Finalizable.Finalize()
- 这些一起使该结构接收 Finalizable
接口(interface)。然后是一个函数func MakeRef(obj Finalizable) *Reference
,它返回一个指向结构的指针,该结构接收一个方法func Get() Finalizable
,它获取引用目标,它是通过增加底层对象的引用计数来初始化的,然后设置一个终结器(通过 runtime.SetFinalizer()
)来减少引用。 AbstractCounted
的 Finalizable
实现依次调用结构上的 Finalize()
,当引用计数达到零时嵌入它。
因此,一切都被设置为非常像 Java 中的软引用/引用队列现在的工作方式,除了它是引用计数而不是 Root 于事件词法范围的标记/清除,它正在寻找“软”可访问的东西.
看起来效果不错!但是 - 我想写一个测试用例......
我完全理解终结器调用是延迟的,并且不保证根据 reflect
包文档运行它们。这种情况在其他具有运行时 gc 和终结器(C#、VB、Java、Python 等)的语言中也是相同的。
但是,在所有其他语言中,请求显式 GC(此处通过 runtime.GC()
函数)似乎确实会导致终结器运行。由于在 Go 中不是这种情况,我想不出一种方法来编写将触发终结器的测试用例。
是否有任何技巧或代码片段(我认为这是依赖于当前实现的,即 future 可能会中断!)可以可靠地触发这些终结器以便我可以编写测试?
最佳答案
您无法显式触发终结器,因此您可以管理的最佳方法是确保 GC 以 runtime.GC()
启动,并等待终结器运行。
如果您查看 runtime/mfinal_test.go
,有一些测试通过 channel 等待终结器调用:
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")
}
关于go - Go 中的终结器测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31011765/