Golang runtime.SetFinalizer

原文译文
SetFinalizer sets the finalizer associated with obj to the provided finalizer function. When the garbage collector finds an unreachable block with an associated finalizer, it clears the association and runs finalizer(obj) in a separate goroutine. This makes obj reachable again, but now without an associated finalizer. Assuming that SetFinalizer is not called again, the next time the garbage collector sees that obj is unreachable, it will free obj. SetFinalizer(obj, nil) clears any finalizer associated with obj. The argument obj must be a pointer to an object allocated by calling new, by taking the address of a composite literal, or by taking the address of a local variable. The argument finalizer must be a function that takes a single argument to which obj's type can be assigned, and can have arbitrary ignored return values. If either of these is not true, SetFinalizer may abort the program. Finalizers are run in dependency order: if A points at B, both have finalizers, and they are otherwise unreachable, only the finalizer for A runs; once A is freed, the finalizer for B can run. If a cyclic structure includes a block with a finalizer, that cycle is not guaranteed to be garbage collected and the finalizer is not guaranteed to run, because there is no ordering that respects the dependencies. The finalizer is scheduled to run at some arbitrary time after the program can no longer reach the object to which obj points. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program. For example, an os.File object could use a finalizer to close the associated operating system file descriptor when a program discards an os.File without calling Close, but it would be a mistake to depend on a finalizer to flush an in-memory I/O buffer such as a bufio.Writer, because the buffer would not be flushed at program exit. It is not guaranteed that a finalizer will run if the size of *obj is zero bytes, because it may share same address with other zero-size objects in memory. See https://go.dev/ref/spec#Size_and_alignment_guarantees. It is not guaranteed that a finalizer will run for objects allocated in initializers for package-level variables. Such objects may be linker-allocated, not heap-allocated. Note that because finalizers may execute arbitrarily far into the future after an object is no longer referenced, the runtime is allowed to perform a space-saving optimization that batches objects together in a single allocation slot. The finalizer for an unreferenced object in such an allocation may never run if it always exists in the same batch as a referenced object. Typically, this batching only happens for tiny (on the order of 16 bytes or less) and pointer-free objects. A finalizer may run as soon as an object becomes unreachable. In order to use finalizers correctly, the program must ensure that the object is reachable until it is no longer required. Objects stored in global variables, or that can be found by tracing pointers from a global variable, are reachable. For other objects, pass the object to a call of the KeepAlive function to mark the last point in the function where the object must be reachable. For example, if p points to a struct, such as os.File, that contains a file descriptor d, and p has a finalizer that closes that file descriptor, and if the last use of p in a function is a call to syscall.Write(p.d, buf, size), then p may be unreachable as soon as the program enters syscall.Write. The finalizer may run at that moment, closing p.d, causing syscall.Write to fail because it is writing to a closed file descriptor (or, worse, to an entirely different file descriptor opened by a different goroutine). To avoid this problem, call KeepAlive(p) after the call to syscall.Write. A single goroutine runs all finalizers for a program, sequentially. If a finalizer must run for a long time, it should do so by starting a new goroutine. In the terminology of the Go memory model, a call SetFinalizer(x, f) “synchronizes before” the finalization call f(x). However, there is no guarantee that KeepAlive(x) or any other use of x “synchronizes before” f(x), so in general a finalizer should use a mutex or other synchronization mechanism if it needs to access mutable state in x. For example, consider a finalizer that inspects a mutable field in x that is modified from time to time in the main program before x becomes unreachable and the finalizer is invoked. The modifications in the main program and the inspection in the finalizer need to use appropriate synchronization, such as mutexes or atomic updates, to avoid read-write races. SetFinalizer将与obj相关的finalizer设置为提供的 终结器函数。当垃圾收集器发现一个不可达的块 时,它会清除该关联,并在一个单独的程序中运行 finalizer(obj)在一个单独的goroutine中运行。这使得obj可以再次到达。但现在没有相关的终结者。假设SetFinalizer 不再被调用,下次垃圾收集器看到 obj是不可及的,它将释放obj。 SetFinalizer(obj, nil)清除了与obj相关的任何终结者。 参数obj必须是一个指针,指向通过调用 新分配的对象的指针,或通过获取复合字面的地址,或通过获取局部变量的地址。一个局部变量的地址。参数finalizer必须是一个函数,需要一个单一的参数 并可以有任意的忽略的返回值。值。如果这两点都不对,SetFinalizer可能会中止 程序。 终结者是按依赖顺序运行的:如果A指向B,则两者都有 终结器,并且它们都无法到达,那么只有A的终结器可以运行。运行;一旦A被释放,B的终结者就可以运行。如果一个循环结构包括一个有终结器的块,那么这个 循环不能保证被垃圾收集,并且最终处理程序 也不能保证运行,因为没有任何排序可以 尊重依赖关系。 终结器被安排在程序不能再到达obj所指向的对象后的某个任意时间运行。我们不能保证终结者会在程序退出前运行,所以通常它们只在长期运行的程序中释放与对象相关的非内存资源时有用。例如,一个os.File对象可以使用一个终结者来关闭相关的操作系统文件描述符,当程序放弃一个os.File而不调用Close时,但依靠终结者来冲刷一个内存中的I/O缓冲区,如bufio.Writer,将是一个错误,因为缓冲区不会在程序退出时被冲刷。 如果*obj的大小为零字节,不能保证终结者会运行,因为它可能与内存中其他零大小的对象共享同一地址。参见https://go.dev/ref/spec#Size_and_alignment_guarantees 不保证终结器会对在包级初始化器中分配的对象运行 在初始化器中分配给包级变量的对象。这些对象可能是 链接器分配的,而不是堆分配的。 请注意,由于终结者可以在对象不再被引用后的很长时间内任意地执行 在一个对象不再被引用之后,运行时被允许执行 一个节省空间的优化,将对象集中在一个单一的 分配槽中。在这样的分配中,一个未被引用的对象的终结器 的终结器可能永远不会运行,如果它总是与被引用的对象存在于同一批次中的话 被引用的对象存在于同一批次。通常情况下,这种批处理只发生在微小的 (在16字节或更少的顺序上)和无指针对象。 一旦一个对象变得不可触及,一个终结者就会运行。为了正确使用终结者,程序必须确保 对象是可以到达的,直到它不再被需要。存储在全局变量中的对象,或者可以通过追踪全局变量的指针找到的 指针找到的对象,都是可达的。对于其他对象。将该对象传递给KeepAlive函数的调用,以标记 在该函数中的最后一点,该对象必须是可到达的。 例如,如果p指向一个结构,如os.File,它包含了 一个文件描述符d,并且p有一个关闭该文件描述符的终结器。描述符,并且如果在一个函数中最后一次使用p是对 syscall.Write(p.d, buf, size)的调用,那么一旦程序进入syscall.Write(p.d),p就可能无法被访问。程序一进入syscall.Write,p就可能无法到达。终结器可能在那一刻运行。关闭p.d,导致syscall.Write失败,因为它正在向一个已关闭的文件描述符写入 因为它正在写入一个已关闭的文件描述符(或者更糟糕的是,写入一个完全不同的 文件描述符)。为了避免这个问题。在调用syscall.Write后调用KeepAlive(p)。 一个goroutine按顺序运行一个程序的所有终结器。如果一个终结器必须运行很长时间,它应该通过启动一个新的goroutine来实现。一个新的goroutine。 用Go内存模型的术语来说,调用 SetFinalizer(x, f) "同步于 "最终化调用f(x)。然而,并不能保证KeepAlive(x)或任何其他使用x "同步于 "f(x),所以一般来说,终结者如果需要使用mutex 或其他同步机制,如果它需要访问x中的可变状态。例如,考虑一个终结者检查x中的一个可变字段 中的一个可变字段,该字段在主程序中时常被修改,在x 变得不可达,最终处理程序被调用之前,主程序会不时地对该字段进行修改。主程序中的修改和终结者中的检查 需要使用适当的同步,如互斥或原子更新。以避免读写竞争。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值