来源:BBAPP体育官网下载 发布时间:2024-12-21 23:30:39
我们写过C语言、C++的朋友都知道,我们的C语言是没有垃圾回收这种观点的。手动分配、释放内存都要求我们的程序员自己完成。不管是“内存泄漏” 还是野指针都是让开发者非常头疼的问题。所以C语言开发这个讨论得最多的话题就是内存管理了。但是对其他高级语言来说,例如Java、C#、Python等高级语言,已经具备了垃圾回收机制。这样做才能够屏蔽内存管理的复杂性,使开发的人能更好的关注核心的业务逻辑。
对我们的Python开发者来说,我们大家可以当甩手掌柜。不用操心它怎么回收程序运行过程中产生的垃圾。但是这毕竟是一门语言的内心功法,难道我们甘愿一辈子做一个API调参侠吗?
当我们的Python解释器在执行到定义变量的语法时,会申请内存空间来存放变量的值,而内存的容量是有限的,这就涉及到变量值所占用内存空间的回收问题。
当一个对象或者说变量没有用了,就会被当做“垃圾“。那什么样的变量是没有用的呢?
当解释器执行到上面这里的时候,会划分一块内存来存储 10000 这个值。此时的 10000 是被变量 a 引用的
当我们修改这个变量的值时,又划分了一块内存来存 30000 这个值,此时变量a引用的值是30000。
这个时候,我们的 10000 已无变量引用它了,我们也可以说它变成了垃圾,但是他依旧占着刚才给他的内存。那我们的解释器,就要把这块内存地盘收回来。
上面我们不难发现了什么是程序运行过程中的“垃圾”,那如果,产生了垃圾,我们不去处理,会产生什么样的后果呢?试想一下,如果你家从不丢垃圾,产生的垃圾就堆在家里会怎么呢?
上面的结果其实就是计算机里面让所有程序员都闻风丧胆的问题,内存溢出和内存泄露,轻则导致程序工作速度减慢,重则导致程序崩溃。
内存溢出:程序在申请内存时,只有少数的内存空间供其使用,出现 out of memory
内存泄露:程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光
前面我们提到过垃圾的产生的是因为,对象没有再被其他变量引用了。那么,我们的解释器究竟是怎么知道一个对象还有没有被引用的呢?
答案就是:引用计数。python内部通过引用计数机制来统计一个对象被引用的次数。当这个数变成0的时候,就说明这个对象没有被引用了。这样一个时间段它就变成了“垃圾”。
熟悉c语言或者c++的朋友,看到这一个应该特别熟悉,他就是结构体。这是因我们Python官方的解释器是CPython,它底层调用了很多的c类库与接口。所以一些底层的数据是通过结构体进行存储的。看不懂的朋友也没关系。这里,我们只要关注一个参数:`ob_refcnt`这个参数非常神奇,它记录了这个对象的被变量引用的次数。所以上面 hello,world 这个对象的引用计数就是 1,因为现在只有text这个变量引用了它。**①变量初始化赋值:**```pythontext = hello,world
此时 hello,world 对象的引用计数为:0,被当成了垃圾。下一步,就该被我们的垃圾回收器给收走了。
注意:当使用某个引用作为参数,传递给 getrefcount() 时,参数实际上创建了一个临时的引用。因此,getrefcount() 所得到的结果,会比期望的多 1
Python通过引用计数的方法来说实现垃圾回收,当一个对象的引用计数为0的时候,就进行垃圾回收。但是如果只使用引用计数也是有点问题的。所以,python又引进了标记-清除和分代收集两种机制。
Python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。
Python语言默认采用的垃圾收集机制是“引用计数法 ”,该算法最早George E. Collins在1960的时候首次提出,50年后的今天,该算法依然被很多编程语言使用。
引用计数法:每个对象维护一个字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数加1,每当该对象的引用失效时计数减1,一旦对象的引用计数为0,该对象立即被回收,对象占用的内存空间将被释放。
看上面的例子,明明两个变量都删除了,但是这两个对象却没有正真获得释放。原因是他们的引用计数都没有减少到0。而我们垃圾回收机制只有当引用计数为0的时候才会释放对象。这是一个没有办法解决的致命问题。这两个对象始终不会被销毁,这样就会导致内存泄漏。
那怎么解决这一个问题呢?这个时候 标记-清除 就排上了用场。标记清除能处理这种循环引用的情况。
可达(活动)对象:从root集合节点有(通过链式引用)路径达到的对象节点
不可达(非活动)对象:从root集合节点没有(通过链式引用)路径到达的对象节点
标记-清除是一种周期性策略,相当于是一个定时任务,每隔一段时间进行一次扫描。
并且标记-清除工作时会暂停整个应用程序,等待标记清除结束后才会恢复应用程序的运行。
分代回收建立标记清除的基础之上,因为我们的标记-清除策略会将我们的程序阻塞。为减少应用程序暂停的时间,Python 通过“分代回收”(Generational Collection)策略。以空间换时间的方法提高垃圾回收效率。
分代的垃圾收集技术是在上个世纪 80 年代初发展起来的一种垃圾收集机制。
Python 将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python 将内存分为了 3“代”,分别为年轻代(第 0 代)、中年代(第 1 代)、老年代(第 2 代)。
其实,既然我们最终选择了python,性能就不是最重要的了。我相信大部分的python工程师甚至都还没遇到过性能问题,因为现在的机器性能可以弥补。而对于内存管理与垃圾回收,python提供了甩手掌柜的方式让我们更关注业务层,这不是更符合人生苦短,我用python的理念么。如果我还需要像C++那样小心翼翼的进行内存的管理,那我为什么还要用python呢?咱不就是图他的便利嘛。所以,放心去干吧。越早下班越好!
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
余承东:鸿蒙智行尊界S800价格仍未敲定!定位目标是按照1000多万的车的标准来设计,超越传统豪华车品牌
马刺加时险胜老鹰:文班42+6+5+5三分 保罗制胜三分特雷杨23+16
妈妈要去煮饭让姐姐先抱着弟弟,姐姐:妈妈,我快坚持不住了,你别哭啊,你哭我也会哭
妈妈不在家爸爸带娃,童言无忌,老爸当真,爸爸:真是我好大儿,真不白养活
弟弟抢姐姐的资料,开开心心的被姐姐追,最后挨了一脚,网友:抢姐姐东西的目的是为了挨这两脚