Go 性能优化技巧 10/10

垃圾回收不是万能的,Go 一样存在资源泄露问题。

1 SetFinalizer

虽然垃圾回收器能很好地处理循环引用,可一旦加上 SetFinalizer,事情就不那么美妙了。



显然,这些对象并未被释放。在标准库文档里有这样的描述:

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.

好在这类状况并不常见,SetFinalizer 最大的问题是延长了对象生命周期。在第一次回收时执行 Finalizer 函数,且目标对象重新变成可达状态,直到第二次才真正 “销毁”。这对于有大量对象分配的高并发算法,可能会造成很大麻烦。

SetFinalizer sets the finalizer associated with x to f. When the garbage collector finds an unreachable block with an associated finalizer, it clears the association and runs f(x) in a separate goroutine. This makes x reachable again, but now without an associated finalizer. Assuming that SetFinalizer is not called again, the next time the garbage collector sees that x is unreachable, it will free x.

2 Goroutine Leak

无论是同步通道(channel),还是带缓冲区的异步通道。当条件不满足时,都会进入等待队列休眠,直到被另一方唤醒。可如果没有被唤醒,那么会出什么问题?


这是个极简单的演示,我们注释掉数据读取方,让发送方全部进入休眠等待状态。按理说,当 test 执行结束后,通道 c 已超出作用域,理应被释放回收,但实际情况是:


这些处于 “chan send” 状态的 G 对象(goroutine)会一直存在,直到唤醒或进程结束,这就是所谓的 “Goroutine Leak”。解决方法很简单,可设置 timeout。或定期用 runtime.Stack 扫描所有 goroutine 调用栈,如果发现某个 goroutine 长时间(阈值)处于 “chan send” 状态,可用一个类似 “/dev/null hole” 的接收器负责唤醒并 “处理” 掉相关数据。



任何机制都有覆盖不到的地方,这就要求我们对底层的某些实现方式有所了解,同时进行严格测试。


本系列结束

下个选题未定,可能会发一些零散技巧过渡。


请关注微信公众号


推荐阅读更多精彩内容

  • 文章作者:Tyan博客:noahsnail.com | CSDN | 简书 Item 7: Avoid f...
    SnailTyan阅读 210评论 4 0
  • 一、奇迹 1、一早有个想法想去紫娟那里喝茶,不过等我修完手机,已经来不及去了,谁知我老公给我电话说,晚上要不要去喝...
    我是喜悦阅读 39评论 0 0
  • 曾经拥有过的爱情,我不敢这样去说,那是因为我记得住这样的歌词“你在他的怀里,却不一定在他心里”。就好像我曾经笃信...
    guoli阅读 196评论 3 3
  • 姜野上班的第一天,办公室异常的安静。 有人敲门。姜野看了一眼是杜冲,准备起身让他进来,无奈病去如抽丝挺了一下又做到...
    涂鸦徒阅读 837评论 1 1
  • 就像南北战争美国南方的旧影,温柔的绅士与贤惠的淑女。相似与互补,失去与珍惜,骑士与红土地,只不过是一段随风消逝的梦...
    春衫袖阅读 80评论 1 0