python 释放变量所指向的内存_Go 的 GC 不怕变量间循环依赖

作为一个写 C++ 入行的工程师,我初次遇到的内存管理(Memory Management )便是 shared_ptr。和普通 pointer 相比,shared_ptr 会维护一个 reference count,当 reference count 等于零的时候,shared_ptr 便会释放所维护的内存。

shared_ptr 有一个缺点是它不太容易处理循环依赖(circular dependency/reference)。即 A 依赖 B,B 依赖 A。当主程序没有变量依赖他俩的时候,A 和 B 的资源应该被释放。但是由于他们的 reference count 都为 1,所以 shared_ptr 不会将其释放,从而导致内存泄漏。一般的解法是引入 weak_ptr,这些都需要在设计时由程序员自己考虑。

在测试 C++ 程序的时候如果发现内存泄漏,我总会先考虑两种可能:(1)是不是哪里的 new 没有被 shared_ptr 包住;(2)是不是哪里有 shared_ptr 的循环依赖。

循环依赖这个问题也困扰着 Python。Python 是自带内存管理的,实现方法与 shared_ptr 类似,是在底层 C 的 PyObject 中维护一个 reference count。

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt; // object reference count
    PyTypeObject *ob_type;
} PyObject;

由于实现机制类似,Python 也会有循环引用导致内存泄漏的问题。Google "python circular reference" 就能找到很多对应的抱怨。

由于 C++ 和 Python 的经历,我有了两个错觉:(1)自带内存管理的语言或多或少都是按照 reference counter 实现的;(2)循环依赖是坑,千万不要碰。

直到前几天看了 Go garbage collector 的介绍文章,我才意识到这两个错觉是错觉。

As of version 1.12, the Go programming language uses a non-generational concurrent tri-color mark and sweep collector.

Go GC 的实现类型不是 reference count,而是 mark and sweep。简单来说,在程序运行的过程中,Go GC 会时不时执行 Mark 和 Sweep。

  1. Mark:对于每一个正在执行的 Goroutine,GC 会扫描其 stack 获得所有的 pointer 变量(以及 pointer 的 pointer 等),并在内存中 mark 这些 pointer 对应的内容。
  2. Sweep:扫描整片内存并回收没有被 mark 的部分。

可以看到 Go GC 不关心 reference count。在之前循环引用的例子中,只要所有 Goroutine 的 stack 上没有 pointer 指向 A/B,A/B 就会被回收。所以 Go 没有循环依赖的问题。

Reference

  1. Garbage Collection In Go

2. Visualizing Garbage Collection Algorithms

3. The Journey of Go's Garbage Collector

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值