什么是垃圾回收
垃圾回收是指在程序运行过程中,通过某种机制删除不再需要使用的对象,释放其使用的内存资源。
为什么需要垃圾回收
- 内存资源有限且珍贵,如果资源不再使用了,应当尽早把其占用的内存空间腾出来。
- 若程序编写不当,使用内存资源时仅申请而不释放,会造成“内存泄漏”,现象是程序占用的内存不断增加。
- 编程语言如果提供了自动的垃圾回收机制,开发者在开发过程中能够适当减少对内存申请、释放的关注,把更多的精力投入业务逻辑的开发中。
Python语言如何进行垃圾回收
- 引用计数法(Reference Counting)
- 追踪法 / 标记删除法(Tracing / Mark and Sweep)
- 分代回收(基于弱代假设)
什么是引用计数法
存储对象时,除了存储对象的类型和值外,同时还存储一个计数器其被引用的次数,在对象被引用时该计数器递增一,在对象的某一引用关系被解除后,该计数器的值递减一,一旦对象的引用计数递减为零,则删除该对象,释放其占用的内存空间。
什么时候引用计数递增一
- 对象被创建时
- 对象被添加都容器中时
- 对象被作为参数传递到函数中时
什么时候引用计数递减一
- 显示使用del语句删除对象时
- 对象被移出容器时
- 函数参数或局部变量在函数调用结束时
引用计数法的优点
- 实现简单
- 实时生效,对象的引用计数一旦递减为零,立刻就被回收
- 垃圾回收的操作被分散在程序运行的整个过程中
引用计数法的缺点
- 对所有对象都引入了一个额外的计数变量,程序有了额外的开销
- 程序运行中需要不断对所有对象的引用计数进行递增、递减的操作
- 并发访问计数变量时可能会发生错误,因此非线程安全的,Python语言为了解决这个问题引入了GIL(Global Interpreter Lock)
- 无法回收不再使用的但是存在循环引用的对象
什么是追踪法 / 标记删除法
指从“GC根节点”出发,通过引用关系追踪找出所有“可达”的对象,标记出存活的对象(即可达的对象),删除所有已死的对象(即不可达的对象),在内存中的对象数量超过特定阈值后,会启动该回收机制。
追踪法/标记删除法的优点
- 解决了存在循环引用的对象的回收问题
追踪法/标记删除法的缺点
- 无法实时触发,只能定时或在对象达到某一阈值后执行垃圾回收
- 如果对象很多,垃圾回收对性能可能有较大的损耗
什么是分代回收
分代回收是一种追踪法垃圾回收的一种,基于“大多数对象在年轻的时候就会被回收”的假设(类似已经流传了更多年的书籍比新出版的书籍更可能继续流传下去),对“年轻”的对象进行更高频率的垃圾回收。
- Python将对象分为0、1、2代,分别使用三个列表进行记录。
- 新创建出来的对象会被存放到0代列表
- 当某一代对象触发垃圾回收时,比该代小的代也会被进行垃圾回收(因此“年轻”的对象被更高频地检测回收)
- 在一次垃圾回收中存活下来的对象会被移入下一代,直到最大的2代。
什么时候触发
- 垃圾回收算法会在列表内的对象数目超过特定的阈值后执行
分代回收的优点
- 解决了标记删除法每次进行垃圾回收时针对所有对象进行垃圾回收可能导致较大的性能损耗的问题