使用 uintptr 作为弱引用是否安全

Is it safe to use a uintptr as a weak reference

我想在 Go 中实现一个弱引用,这样我就可以使用终结器来检测何时不再需要数据结构并能够 store/clean 更新数据。

我发现的一种方法是使用 uintptr 作为映射键,这样当调用终结器时,我可以 access/cleanup 使用传递给终结器函数的指针值来获取数据。这样做安全吗?

我想我的问题是:Go 使用移动垃圾收集器吗?还是会?

unsafe documentation 允许 GC 在内存中移动值。

A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.

The link that you yourself quoted 提供了您要问的问题的答案:不是给客户一个指向您想要大惊小怪的底层对象的指针,而是给他们一个指向包装对象的指针:

type internal struct {
    // all the internal stuff goes here
}

// change the name Wrapper below to something more suitable
type Wrapper struct {
    *internal   // or p *internal if you want to be overly verbose
}

func NewWhatever(/*args*/) *Wrapper {
    p := &Wrapper{...} // fill this part in
    runtime.SetFinalizer(p, wrapperGotCollected)
    return p
}

func wrapperGotCollected(p *Wrapper) {
    // since p itself is about to be collected,
    // **p (or *((*p).p)) is no longer accessible by
    // the user who called NewWhatever().  Do
    // something appropriate here.
}

请注意,这 不是 *internal 上使用终结器,而是在 *Wrapper 上使用终结器:当时 wrapperGotCollected 被调用时,*internal 对象本身保证仍然存在,因为 p 本身还没有被 GC-ed(它已经进行了一半,并且将在那里的其余部分作为wrapperGotCollected returns).

不久或之后不久

为了完整起见,这就是我最后做的事情:

type actualThing struct {
    Data int
}

type internalHandle struct {
    *actualThing
}

type ExternalHandle struct {
   *internalHandle
}

所以 ExternalHandle 的用户仍然可以编写像 ExternalHandle.Data 这样的代码,但内部维护代码仍然可以自动更新 *actualThing。指向 ExternalHandle 指针的终结器将向堆栈的其余部分发出信号,删除对 internalHandle 的引用并停止向其传播更新。

基本上超级方便的事情是,通过嵌套结构,ExternalHandle 的用户在使用它时没有意识到或处理它是双指针取消引用的事实。