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
接口。然后有一个函数func MakeRef(obj Finalizable) *Reference
返回一个指向结构的指针,该结构接收一个方法func Get() Finalizable
,它获取引用目标,并通过增加底层对象的引用计数来初始化它,然后设置一个终结器(通过runtime.SetFinalizer()
)将减少引用。 AbstractCounted
的Finalizable
实现反过来调用Finalize()
当引用计数达到零时嵌入它的结构。
因此,一切都设置为非常类似于 Java 中的软引用/引用队列现在的工作,除了它是引用计数,而不是植根于活动词法范围的标记/扫描,它正在寻找“软”可访问的东西。
它似乎工作得很好!但是 - 我想写一个测试用例......
我完全理解终结器调用被推迟,并且不保证根据reflect
包文档运行它们。在其他具有运行时 gc 和终结器(C#、VB、Java、Python 等)的语言中,情况也是如此。
然而,在所有其他语言中,请求显式 GC(这里通过runtime.GC()
函数)似乎确实会导致终结器运行。由于在 Go 中不是这种情况,我无法找到一种方法来编写将触发终结器的测试用例。
是否有任何技巧或代码片段(我认为这是依赖于当前实现的,即将来可能中断!)可以可靠地触发这些终结器,以便我可以编写我的测试?