Python核心技术与实战
景霄
Facebook资深工程师
立即订阅
13891 人已学习
课程目录
已完结 46 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 从工程的角度深入理解Python
免费
基础篇 (14讲)
01 | 如何逐步突破,成为Python高手?
02 | Jupyter Notebook为什么是现代Python的必学技术?
03 | 列表和元组,到底用哪一个?
04 | 字典、集合,你真的了解吗?
05 | 深入浅出字符串
06 | Python “黑箱”:输入与输出
07 | 修炼基本功:条件与循环
08 | 异常处理:如何提高程序的稳定性?
09 | 不可或缺的自定义函数
10 | 简约不简单的匿名函数
11 | 面向对象(上):从生活中的类比说起
12 | 面向对象(下):如何实现一个搜索引擎?
13 | 搭建积木:Python 模块化
14 | 答疑(一):列表和元组的内部实现是怎样的?
进阶篇 (11讲)
15 | Python对象的比较、拷贝
16 | 值传递,引用传递or其他,Python里参数是如何传递的?
17 | 强大的装饰器
18 | metaclass,是潘多拉魔盒还是阿拉丁神灯?
19 | 深入理解迭代器和生成器
20 | 揭秘 Python 协程
21 | Python并发编程之Futures
22 | 并发编程之Asyncio
23 | 你真的懂Python GIL(全局解释器锁)吗?
24 | 带你解析 Python 垃圾回收机制
25 | 答疑(二):GIL与多线程是什么关系呢?
规范篇 (7讲)
26 | 活都来不及干了,还有空注意代码风格?!
27 | 学会合理分解代码,提高代码可读性
28 | 如何合理利用assert?
29 | 巧用上下文管理器和With语句精简代码
30 | 真的有必要写单元测试吗?
31 | pdb & cProfile:调试和性能分析的法宝
32 | 答疑(三):如何选择合适的异常处理方式?
量化交易实战篇 (8讲)
33 | 带你初探量化世界
免费
34 | RESTful & Socket: 搭建交易执行层核心
35 | RESTful & Socket: 行情数据对接和抓取
36 | Pandas & Numpy: 策略与回测系统
免费
37 | Kafka & ZMQ:自动化交易流水线
38 | MySQL:日志和数据存储系统
39 | Django:搭建监控平台
40 | 总结:Python中的数据结构与算法全景
技术见闻与分享 (4讲)
41 | 硅谷一线互联网公司的工作体验
42 | 细数技术研发的注意事项
加餐 | 带你上手SWIG:一份清晰好用的SWIG编程实践指南
43 | Q&A:聊一聊职业发展和选择
结束语 (1讲)
结束语 | 技术之外的几点成长建议
Python核心技术与实战
登录|注册

24 | 带你解析 Python 垃圾回收机制

景霄 2019-07-03
你好,我是景霄。
众所周知,我们当代的计算机都是图灵机架构。图灵机架构的本质,就是一条无限长的纸带,对应着我们今天的存储器。在工程学的演化中,逐渐出现了寄存器、易失性存储器(内存)和永久性存储器(硬盘)等产品。其实,这本身来自一个矛盾:速度越快的存储器,单位价格也越昂贵。因此,妥善利用好每一寸高速存储器的空间,永远是系统设计的一个核心。
回到 Python 应用层。
我们知道,Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。
而对于服务器,这种设计为永不中断的系统来说,内存管理则显得更为重要,不然很容易引发内存泄漏。什么是内存泄漏呢?
这里的泄漏,并不是说你的内存出现了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已不再使用的内存。
内存泄漏也不是指你的内存在物理上消失了,而是意味着代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费。
那么,Python 又是怎么解决这些问题的?换句话说,对于不会再用到的内存空间,Python 是通过什么机制来回收这些空间的呢?
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Python核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(18)

  • Jingxiao 置顶
    思考题答案:

    事实上算法可以写的很简单,这是个很经典的 dfs (深度优先搜索)遍历,从起点开始遍历,对遍历到的节点做个记号。遍历完成后,再对所有节点扫一遍,没有被做记号的,就是需要垃圾回收的。
    2019-07-06
    7
  • Switch
    思考题:
    from typing import Set


    class Graph:
        def __init__(self, value, nodes=None):
            self._value = value
            self._nodes: list = [] if nodes is None else nodes

        @property
        def value(self):
            return self._value

        @property
        def nodes(self):
            return self._nodes

        def node_add(self, node):
            self._nodes.append(node)

        def node_adds(self, nodes):
            self._nodes.extend(nodes)

        def node_del(self, node):
            self._nodes.remove(node)

        def __str__(self):
            return "Graph {} nodes {}".format(self._value, [node.value for node in self.nodes])

        def __repr__(self):
            return self.__str__()


    def dfs(start: Graph, includes: Set[Graph] = None) -> Set[Graph]:
        if includes is None:
            includes = set()
        if start in includes:
            return includes
        includes.add(start)
        for s in start.nodes:
            includes.update(dfs(s, includes))
        return includes


    if __name__ == '__main__':
        A = Graph('A')
        B = Graph('B')
        C = Graph('C')
        D = Graph('D')
        E = Graph('E')
        F = Graph('F')
        has_nodes = {A, B, C, D, E, F}

        # A->B->E
        # ->C->E
        # B->A
        # D->F
        # F->D
        A.node_adds([B, C])
        B.node_adds([A, E])
        C.node_adds([E])
        D.node_adds([F])
        F.node_adds([D])
        graph_nodes = dfs(A, set())
        # OUT: {Graph B nodes ['A', 'E'], Graph E nodes [], Graph C nodes ['E'], Graph A nodes ['B', 'C']}
        print(graph_nodes)
        # OUT: {Graph F nodes ['D'], Graph D nodes ['F']}
        print(has_nodes - graph_nodes)
    2019-07-08
    1
    10
  • 程序员人生
    请问一下,老师
    执行关于objgraph代码,出现如下错误:
    Graph viewer (xdot) and image renderer (dot) not found, not doing anything else
    是不是还要安装什么软件?
    2019-07-03
    6
    4
  • 陈迪🐶
    1. 循环引用情况下Python不立即回收内存,如果放任不管,即不显式调用gc.collect的话,Python的垃圾回收器自己会什么时候处理?
    2. 最后介绍了内存泄露排查工具,哪种算内存泄露呢?接问题1,不立即回收算内存泄露吗?还是有其他场景

    作者回复: 分代收集算法中每一代都有一个默认阈值,超过指定阈值之后就会启动垃圾回收。如果垃圾回收启动太频繁,会造成程序性能低下,分代收集也是为了提高性能,因此不立刻回收没关系,只要一定时间或者一定阈值之后回收都没问题。内存泄漏是这部分内存永远不再被回收,越攒越多,直到撑爆内存。

    2019-07-03
    2
    2
  • 天凉好个秋
    本文讲的垃圾回收算法在Java中也都有,当初在设计的时候是不是参考了Java?而且,Java中还有标记整理算法,可以解决回收内存不连续的问题,这个在Python中有没有考虑呢?

    作者回复: 程序语言设计的时候肯定会有互相参考,Java 中的 gc 就很丰富了,程序员可以根据需要配置适合自己应用的 gc 算法,甚至在 OpenJDK 开源后,可以在更深的层次上对本来不对用户开放的细节进行深入配置。python 则并不希望编写者们对这里有过深入了解,拿来开箱即用就行了,这也是 python 设计哲学的一部分,牺牲一定性能换取方便性。

    2019-07-03
    1
    2
  •   星豪
    1. 在读文章的时候找了一个可能是错别字的地方,在循环引用那一节中,第四段试想一下,如果这段代码出现在生产环境中...但经过长时间运行“候”...。这一侯应该是后来的后吧?
    2. 当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。这一句话不是很明白,新增对象我理解的是新创建的对象或者是从上一代挪过来的对象,那么删除对象指的是哪些呢?或者说是如何进行指定哪些是应该被删除的对象呢?

    作者回复: 1. 谢谢
    2. 新增对象指代你创建一个对象,删除对象指代对象被释放,例如手动调用 del,从函数中返回临时变量的释放等,对这两者进行计数统计,然后相减。

    2019-07-04
    1
  • 瞌睡的咸鱼
    思考题——通过有向图的拓扑排序可以求出(可以参考《算法导论》去理解)
    2019-07-03
    1
  • 干布球
    请问老师,问什么多次调用print(sys.getrefcount(a)),只有第一次会增加a的计数呢?
    2019-07-03
    2
    1
  • wnz27
    gc.collect(),不是清除没有引用的对象吗,为什么循环引用代码里没有del a和del b可以垃圾回收呢?是老师手误吗?
    2019-12-03
  • 建强
    简单写了一个有向图的遍历程序,说明:
    (1)有向图采用邻接链表存储
    (2)有向图采用递归算法进行深度优先遍历
    #邻接链表节点定义
    class LinkNode:
        def __init__(self,data):
            self.data = data
            self.next = None

    #邻接链表定义
    class AdjoinList:
        def __init__(self):
            self.adjlist = {} #存贮节点间的邻接关系
            self.vistlist = {} #存贮节点是否被访问,初始所有节点都是False

        #建立结点x1和x2的连接关系,其中,x1指向x2
        def addNode(self,x1,x2):

            #建立链接
            point = self.adjlist.setdefault(x1, None)
            lnode = LinkNode(x2)
            lnode.next = point
            self.adjlist[x1] = lnode
            self.adjlist.setdefault(x2, None)

            #初始化访问列表
            self.vistlist.setdefault(x1,False)
            self.vistlist.setdefault(x2,False)

        #遍历图中所有结点
        def visitNode(self,nodevalue):
            if nodevalue is not None:
                self.vistlist[nodevalue] = True
                print('visist:', nodevalue)
                nextnode = self.adjlist[nodevalue]
                    
                while nextnode is not None:

                    if not self.vistlist[nextnode.data]:
                        self.visitNode(nextnode.data)
                    
                    nextnode = nextnode.next


        #输出未被访问的结点
        def showUnvistedNode(self):
            print('UnvistedNode:', ','.join([x for x in self.vistlist if not self.vistlist[x]]))

    #主程序
    def main():
    #BEGIN

        adlist = AdjoinList()

        #创建有向图,有向图简单示意如下:
        '''
        a-->b-->c
        |
        |
        d-->e-->f

        g-->h-->j
        
        '''
        adlist.addNode('a','b')
        adlist.addNode('a','d')
        adlist.addNode('b','c')
        adlist.addNode('d','e')
        adlist.addNode('e','f')
        adlist.addNode('g','h')
        adlist.addNode('h','j')

        #从a节点开始遍历有向图
        adlist.visitNode('a')

        #输出未访问的节点
        adlist.showUnvistedNode()

    #END

    if __name__ == '__main__':
        main()
    2019-11-02
  • 自由民
    思考题,就是图的遍历算法,深度优先或广度优先算法,标记每个节点,遍历后,没标记过的就是可以回收的垃圾。
    2019-10-17
  • 夏尔
    def func():
        show_memory_info('initial')
        a = [i for i in derange(10000000)]
        show_memory_info('after a created')
        return a

    a = func()
    show_memory_info('finished')

    ########## 输出 ##########

    initial memory used: 47.96484375 MB
    after a created memory used: 434.515625 MB
    finished memory used: 434.515625 MB

    这是本文第四个代码块,for i in range;多了de,变成了derange;
    2019-09-06
  • 小侠龙旋风
    老师,请问:除了循环引用,还有哪些错误的程序设计会造成内存泄漏,需要强制调用gc.collect()来垃圾回收?
    2019-07-07
  • 陈迪🐶
    可否给一个内存泄露的实例,逃脱了gc的“法眼”?
    2019-07-06
  • KaitoShy
    请问一下,老师:
    import sys

    a= [1]

    print(sys.getrefcount(a))

    b = a
    print(sys.getrefcount(a))

    c,d,e,f,g,h = b,b,c,e,d,a
    print(g)
    print(sys.getrefcount(a))
    -------


    import sys...
    2
    3
    [1]
    6
    为什么最后一次是6次?而g的值是正常的呢?
    2019-07-04
    1
  • 响雨
    思考题看的我一脸蒙蔽,无从下手
    2019-07-03
  • 舒服
    什么是函数调用栈
    2019-07-03
  • ikimiy
    python的垃圾回收相对于Java来说是不是简单很多,Java 的垃圾回收好像不再使用引用计数了是吗?Java 有很多种垃圾收集器,比如G1,CMS等,python的垃圾收集器也分很多种的么!
    2019-07-03
    1
收起评论
18
返回
顶部