V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
skinny
V2EX  ›  Python

Python 内存占用也太大了。

  •  1
     
  •   skinny · 2018-09-25 10:12:15 +08:00 · 22656 次点击
    这是一个创建于 2281 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我现有 5 个 collections.Counter 的 pickle 文件,单个文件在 84MB-240MB 之间,总共 664MB,总记录在 3 千多万,实际数据大小也就 400MB 左右。之所以分为 5 个文件是因为之前有几个 GB 的数据,我电脑内存小( 6GB ),又是机械硬盘,根本没办法一下子读取和处理,我分割成小块处理,最后变成了这 5 个文件,然后我想合并这些文件进行最后处理。

    最终合并前,我预估过用 C 实现类似的字典( D[char[10],uint])合并,内存用的不会很多,即便是最粗糙的字典实现也只需要 680M 左右内存,我看 pickle 文件加起来才 664MB,就算翻 4 翻的内存占用机器也撑得住,可是一运行内存就被耗光,然后机器死机了,只能强制关机,根据死机前的 Python 占用情况,最终可能需要内存要 6GB-8GB 才能加载处理总共 664M 的这 5 个 pickle 文件。(只有 load 和 plus 操作)

    可能有人会问我为什么不用 Redis 或者数据库查询,因为我没安装,我也就这一次需要以这种方式处理这种文件。前面用 Python 处理单个小文件时还好,虽然速度不敢恭维,不过还能接受,胜在写起来简单方便。

    目前对 Python 感受就是慢、吃内存,但是写起来简单(当然也有非常复杂的,比如 asynio ),真的是胶水一样的语言。

    第 1 条附言  ·  2018-09-25 11:35:48 +08:00
    我吐槽的不是 pickle,而是 Python 对象内存占用太多(比如 str,dict 等等),某些人看清再 diss 不迟
    第 2 条附言  ·  2018-09-25 15:02:54 +08:00
    本来就只是吐槽 Python 对象内存占用大得超过我的预期(需要数据的十倍以上内存),莫名其妙就有一些人给我定了几个不存在的靶子来 diss 我。

    1. 首先,这帖子不是在提问,所以所有基于“楼主的帖子是个提问帖,且是糟糕的提问”来攻击我不会问问题的可以歇歇了。

    2. 整个主贴里,pickle 只是描述使用场景的一部分细节,且明确的说明了整个代码只有 pickle 的 load 和 plus 操作,甚至容器内数据的类型都说了,而且,从头到尾我没有说是 pickle 导致的内存占用过高、死机,所以基于“楼主错误的使用了 pickle ”来攻击的可以歇歇了。

    3. 说我把 pickle 用在错误的场景的,首先,这是一次性任务,在有限的条件下,我只是需要一个方便快速的 save and load 媒介,除了 pickle,我当然也可以用 json、xml、sqlite 等等标准库、甚至可以用某些人说的文本文件,可问题是我并没有说 pickle 导致的内存占用过多(得强调多少次),甚至在处理这个任务期间这是最方便的标准库。再有,之前经过几轮数据分解,最终变成了这主贴里描述的这样 5 个文件,就算使用文本文件,最终还是需要读取到内存放入到 collections.Counter 的 [看到了 pickle 却看不到 collections.Counter ?] ,已经经过多轮处理,就算依然有高达百分之 30 的重复率,也有 1200 万左右数据,根据宕机前使用率、增长率也需要 4.2GB-5.6GB 内存。实际数据 400MB 左右,你们看看 Python 对象内存占用?

    4. 我只是 [吐槽] Python 对象的内存占用超过预估的好几倍,且主贴里说的够清楚了:
    a. 有 3000 多万条数据,分别在 5 个 collections.Counter 序列化文件里
    b. 字典的 K 是长度为 10 的字符串(实际是 3-10 ),V 是多轮数据分解下来的重复次数统计。
    c. 只使用了 pickle 的 load 和 plus 操作
    d. 实际数据 400MB 左右
    e. 针对说我错误使用 pickle,不用文本文件为由来喷的,你们把数据量减少 30%吧
    说我不给测试用例就瞎喷的,你们眼睛没事吗?

    5. 自己立了个不存在靶子就来攻击的,到底谁沟通能力、理解能力堪忧?

    6. 至于说为什么不用 sqlite,我试过了,可是太慢取消了,这个你们可以 diss 我不擅长使用 SQL 相关,甚至可以 diss 我穷,买不起大内存和固态硬盘,不过拜托 diss 我也 diss 对点好吗?跟 pickle 毛关系啊!跟我吐槽 Python 对象内存占用高毛关系啊!

    7. 还有说其它语言序列化反序列化内存占用的,首先我没有吐槽序列化占用高,你们完全可以把 pickle 替换成别的方法(比如保存到文本文件,逐行读取),但是总归有 5 个 collections.Counter 对象保存着 3000 多条万数据,你可以减去个 30%,如果你觉得这样可以证明我在胡说八道。

    8. 如果有人一定要以我不用 Redis 或 数据库(随便哪个)来 diss 我,说我不会用胡说八道,请认真看看主贴,再看看追加的 6。

    再有莫名其妙的就不回复了,实际上很多莫名其妙的反驳就感觉是在说 Python 对象内存占用多,但是你不能说,说了你就是不会用。我当然可以通过 C 扩展和其它方法节省内存,可我吐槽的不是 Python 对象内存占用超过预期吗?至少 10 倍起步吗?
    第 3 条附言  ·  2018-09-25 15:14:13 +08:00
    如果有人要问我,到底超过预期的多少倍,你随意吧。
    98 条回复    2018-09-27 10:42:05 +08:00
    zhengxiaowai
        1
    zhengxiaowai  
       2018-09-25 10:19:09 +08:00
    没懂、、、
    LokiSharp
        2
    LokiSharp  
       2018-09-25 10:22:05 +08:00
    这种情况用 C 写扩展啊。。。。
    zk8802
        3
    zk8802  
       2018-09-25 10:22:59 +08:00 via iPhone
    用 PyPy 试试。
    shyling
        4
    shyling  
       2018-09-25 10:23:36 +08:00
    别用 pickle
    simonliu2018
        5
    simonliu2018  
       2018-09-25 10:32:52 +08:00
    从磁盘文件 load 到内存里体积都会变大,换做其他序列化方式也是这样,这不是 python 特有的。

    换个角度思考这个问题,各种资源总是有限的,工程师要做的就是在各种资源受限的情况下把事情搞定。
    Rehtt
        6
    Rehtt  
       2018-09-25 10:34:58 +08:00 via Android
    用 golang
    songdg
        7
    songdg  
       2018-09-25 10:36:32 +08:00
    试一下 pytable。
    ltoddy
        8
    ltoddy  
       2018-09-25 10:38:37 +08:00
    去看看 linecache. 还有遇到大的东西,尽量用增量的方式去解决.
    xiaoheijw
        9
    xiaoheijw  
       2018-09-25 10:42:05 +08:00   ❤️ 1
    pickle 是对象序列化,当然吃内存了,大文件建议用 hdf5。
    kios
        10
    kios  
       2018-09-25 10:43:16 +08:00
    @xiaoheijw 赞同!
    skinny
        11
    skinny  
    OP
       2018-09-25 10:47:52 +08:00
    @shyling
    @xiaoheijw

    我写的一次性处理脚本,以前处理的都是以行或块为单位的,这次第一次处理全部都需要载入处理的,看文件不大,就用了标准库,没想到内存占用大得离谱。
    skinny
        12
    skinny  
    OP
       2018-09-25 10:50:43 +08:00
    @simonliu2018
    @LokiSharp

    知道会内存使用会变得多,没想到的是会这么多,差不多是实际数据的十几倍,预估错了所以没有用 C。
    UN2758
        13
    UN2758  
       2018-09-25 11:05:16 +08:00
    文本反序列化一般 10 倍内存起步吧,python 而言
    lihongjie0209
        14
    lihongjie0209  
       2018-09-25 11:07:07 +08:00
    sqlite 不需要安装吧, 可以试一下
    est
        15
    est  
       2018-09-25 11:11:24 +08:00
    明明是自己不会用 pickle 非得用 pickle。怪到一个语言上?
    lihongjie0209
        16
    lihongjie0209  
       2018-09-25 11:17:20 +08:00
    要不这样, 你发一个测试文件出来, 大家一起用 python 标准库写一遍, 看看能不能在你的硬件条件下实现你的需求, 光说不做测试没意义
    skinny
        17
    skinny  
    OP
       2018-09-25 11:18:12 +08:00 via Android
    @lihongjie0209 用了,太慢了。
    dychenyi
        18
    dychenyi  
       2018-09-25 11:24:10 +08:00
    mmap 了解一下,磁盘映射读取
    skinny
        19
    skinny  
    OP
       2018-09-25 11:30:39 +08:00 via Android
    @est 你这人真是莫名其妙,我从来就没说 pickle 什么事,我只说了 Python 对象内存占用多,对象 load 以后是 Counter 对象,你是不是还要说我不会用 Counter ? Counter 是 dict 子类,是不是还要说我不会用 dict ? dict 里存了太多文本是不是还得说我不会用 str ?
    sunnyadamm
        20
    sunnyadamm  
       2018-09-25 11:38:26 +08:00
    with open() as f 了解一下
    est
        21
    est  
       2018-09-25 11:41:00 +08:00   ❤️ 4
    @skinny 因为会用的人就会给出自己的版本号和环境,pickle 的调用方式,和 profile 的具体卡在哪个调用上的剖析。

    你除了抱怨没有给出任何有用的信息。当然结论就是你不会用了。
    wutiantong
        22
    wutiantong  
       2018-09-25 12:07:23 +08:00
    @skinny 人家 est 所 diss 的是“你不会用 pickle 非要强上才导致的问题不应该怪 python ”,你非要理解成他在袒护 pickle,你这边的沟通理解能力堪忧
    billgreen1
        23
    billgreen1  
       2018-09-25 12:10:16 +08:00
    @est 我觉得会问问题的人,一般会提供环境,版本号,自己的分析,以及疑问等;楼主最多是不太会问问题,你直接得出不会用,这个结论是难以成立的。
    est
        24
    est  
       2018-09-25 12:18:51 +08:00
    @billgreen1 万一我猜对了呢。
    javaWeber
        25
    javaWeber  
       2018-09-25 12:22:35 +08:00
    用 java,上 maven,加 20 个依赖包。。开始写程序。
    GreenHand
        26
    GreenHand  
       2018-09-25 12:38:38 +08:00
    试一下其他语言的对象序列化 /反序列化是否占用内存再来吐槽 Python ?

    你这种情况不需要用到 pickle 吧?只是 string: integer 的映射而已,直接写到文件不行吗?

    ```
    with open('data.txt', 'w') as f:
    for i in xrange(30000000):
    print >>f, '%s:%s' % (str(i), i)
    ```

    最后一行的分隔符(冒号)替换一下保证 key 里面不包括换行符即可。
    SuperMild
        27
    SuperMild  
       2018-09-25 12:42:28 +08:00
    数据库没安装? Python 使用 sqlite 不用安装。
    kernel
        28
    kernel  
       2018-09-25 13:27:59 +08:00
    楼主要反击也容易,直接写个几行 python 小程序里加 100M 个 int 到一个 dict 里看看最终占多少内存
    lance6716
        29
    lance6716  
       2018-09-25 13:29:46 +08:00 via Android
    Python 对象内存占用太多我还是第一次听说… dict 最近几个版本就是优化了占用
    skinny
        30
    skinny  
    OP
       2018-09-25 13:41:08 +08:00
    @billgreen1 我从头到尾没有问问题,只是就使用体验的吐槽而已。
    @wutiantong 你这更加莫名其妙了,从头到尾没有 pickle 什么事情,我主贴里说了,吐槽的是本来不大的数据,载入转换成 Python 对象以后翻了很多翻,究竟是谁沟通能力、理解能力堪忧?
    skinny
        31
    skinny  
    OP
       2018-09-25 13:42:32 +08:00
    @kernel 他们不会去试的。
    sagaxu
        32
    sagaxu  
       2018-09-25 13:44:27 +08:00 via Android
    py 最小的数据类型,一个对象也是几十字节
    kernel
        33
    kernel  
       2018-09-25 13:51:16 +08:00
    @lance6716 在处理大量小数据时,比如在 C 里一个 int32 就是 4 字节,python 里要几十字节
    当然处理大量小数据一般不是 python 的应用场景,反正我是从来没碰到过
    zhzer
        34
    zhzer  
       2018-09-25 13:53:13 +08:00 via Android
    counter 还储存了频率本来就应该大一点啊
    scriptB0y
        35
    scriptB0y  
       2018-09-25 13:56:27 +08:00
    还是看一下为什么会占这么多内存: https://pypi.python.org/pypi/memory_profiler
    wutiantong
        36
    wutiantong  
       2018-09-25 14:03:56 +08:00
    @skinny 不是,你老说没 pickle 什么事,可是你明明就是在用 pickle 做序列化反序列化啊?
    hahastudio
        37
    hahastudio  
       2018-09-25 14:26:10 +08:00
    因为高级语言会需要更多的内存空间去存储内部状态、存储函数指针,按你这么说,现在的高级语言怕不是都要被喷
    6 GB 内存和机械硬盘才是主要矛盾,因为内存的限制,你只能用外排序的类似思想去做统计
    -话说需要压缩内存空间的话,为什么不写 C/C++-
    nooper
        39
    nooper  
       2018-09-25 14:33:17 +08:00   ❤️ 2
    hdf5, blosc. 10g 的能压缩成 1g 的内容。本人已测试。300m-> 20m-30m
    skinny
        40
    skinny  
    OP
       2018-09-25 15:06:47 +08:00
    @kernel 我也是第一次遇到。

    @zhzer 是该大一些,我主贴里一开始是吐槽大得超过预期,只是一些人立了些莫名其妙的靶子来 diss 我。
    skinny
        41
    skinny  
    OP
       2018-09-25 15:07:34 +08:00
    @nooper 下次遇到相同问题用这个。
    skinny
        42
    skinny  
    OP
       2018-09-25 15:22:52 +08:00
    @hahastudio 我知道高级语言会比 C 之类占用更多内存,我没有觉得有什么,一开始有心理准备,我粗糙的设计了下 C 的数据结构,预估了内存占用,然后看 pickle 文件和实际数据都不大,全部变成 Python 对象 collections.Counter[str, int]应该可以接受几倍的内存占用,当时我觉得 3GB 到 4GB 内存是可以搞定的,结果没想到爆了。我有考虑不周和偷懒的问题不存在,甚至还有穷的问题存在,不过一些人不知道他们到底在 diss 什么。
    laike9m
        43
    laike9m  
       2018-09-25 15:24:14 +08:00
    如果 3kw 的 D[char[10],uint] 都要放进内存才能处理,是不是得考虑下算法的优化问题了
    skinny
        44
    skinny  
    OP
       2018-09-25 15:36:54 +08:00
    @laike9m 实际是用 C 根本没问题,顶天了 600MB 左右(粗糙 dict 实现),因为实际数据才 400MB 左右,我吐槽的是内存占用,我根本不在乎快到几分钟内完成,只要在半小时内搞定,不影响我同时在进行的编码任务就行,只要不是过高的 CPU 使用率或是内存占用高到导致其它操作(如编码)不能正常进行就行。一开始我是觉得没问题才这样做的,要是考虑算法,我干嘛用 collections.Counter。
    hahastudio
        45
    hahastudio  
       2018-09-25 15:38:40 +08:00
    @skinny 我猜是因为开始很多人认为 pickle 就是直接把内存 dump 下来?
    顺带一提,你是不是遇到了类似于这边的问题? https://stackoverflow.com/questions/16288936/how-do-i-prevent-memory-leak-when-i-load-large-pickle-files-in-a-for-loop
    lihongjie0209
        46
    lihongjie0209  
       2018-09-25 15:38:55 +08:00
    @skinny 所以我说给一份测试数据然后大家自己跑一跑, 一直在这里说最后还有人怀疑你硬件有问题呢
    skinny
        47
    skinny  
    OP
       2018-09-25 15:54:54 +08:00
    @hahastudio 刚刚验证了你发的那个链接,按那个解决方法试了,结果还是一样。实际上就是太多小对象,Python 对象底层数据结构又比较大。
    skinny
        48
    skinny  
    OP
       2018-09-25 15:57:50 +08:00
    @lihongjie0209 追加里再次那么细致的说了数据类型、大小、数量、结构,眼神不好?你要不然按#28 楼说的用 range 生成一批数据测试,也没人说你,是不?
    est
        49
    est  
       2018-09-25 16:09:49 +08:00   ❤️ 1
    @skinny 你补充了那么多,比你第一次发帖的还长。你第一次发帖别人 1 楼注解回复了一句:不懂。

    感受下。

    好吧,我们说内存占用大的问题。一句话「 collections.Counter 数据总共 664MB 结果加载进去 6G 内存不够用」 不就完事了么。

    非得说 Python 内存占用大。这和大多数人的经验相比而言是反直觉的。因为基本上能见到的 jvm 的应用起步 8G 内存。py 写的程序 200M 都算非常大的了。
    est
        50
    est  
       2018-09-25 16:15:53 +08:00
    @skinny 从你补充的信息来看, 大量 collections.Counter 数据做 plus 操作(就是合并?)是有额外的 copy 开销的。这里考虑用一些的 inline 的方式去优化。

    你无论用什么语言都会遇到大 dict 合并问题,大 list 合并问题的。
    est
        51
    est  
       2018-09-25 16:19:58 +08:00
    其实最大的可能就是你反序列化之后的对象没有被 gc 掉。依然占着内存。看起来是 5 个 pickle 文件实际内存里存了好几份。

    你试试 del 下那个对象。23333

    或者你 pickle 有内存泄露。
    2owe
        52
    2owe  
       2018-09-25 16:29:07 +08:00
    强制使用 pickle protocol 高版本,pickle.HIGHEST_PROTOCOL ?只能说有改善,核心问题还是如你所知。
    dbw9580
        53
    dbw9580  
       2018-09-25 16:31:30 +08:00
    典型的又要马儿跑,又要马儿不吃草。
    laqow
        54
    laqow  
       2018-09-25 16:38:21 +08:00 via Android
    不用 Counter 和 pickle 不就完了吗
    misaka19000
        55
    misaka19000  
       2018-09-25 16:52:22 +08:00
    怎么感觉楼主好像在喷 Python ?
    lihongjie0209
        56
    lihongjie0209  
       2018-09-25 16:53:31 +08:00
    @skinny 知道这些信息并没有帮助, 写排序算法的时候也知道输入输出, 数据类型, 那么冒泡算法和归并算法就一样了? 你说 python 有什么问题的前提是你的代码要合理. 就你给的这些信息只够过过嘴瘾, diss python 而已, 并没有什么实际意义.

    我已经说第三遍了, 给测试数据, 给需求, 大家都写不出来那是 python 的问题, 大家写的出来那是你的问题.
    wutiantong
        57
    wutiantong  
       2018-09-25 17:39:07 +08:00
    楼主的不友善态度和杠精精神好像传染了我
    我一个没学过 python 的花了点时间搜了一圈 google
    又按照楼主的描述写了点测试,我后面贴一下
    shm7
        58
    shm7  
       2018-09-25 17:40:28 +08:00
    9 楼,大文件存储用 hdf5,h5py -> pytable 都可以用。
    wutiantong
        59
    wutiantong  
       2018-09-25 17:43:15 +08:00   ❤️ 4
    实际运行环境是新版 MBP,python3.7

    ```
    import collections
    import pickle

    def make_cnt(section):
    cnt = collections.Counter()
    for i in range(6000000):
    cnt[str(i +6000000*section)] = i
    return cnt

    if __name__ == '__main__':
    cnt0 = make_cnt(0)
    pickle.dump(cnt0, open('/tmp/cnt0', 'wb'))
    input("Press Enter to continue...")
    cnt1 = make_cnt(1)
    pickle.dump(cnt1, open('/tmp/cnt1', 'wb'))
    input("Press Enter to continue...")
    cnt2 = make_cnt(2)
    pickle.dump(cnt2, open('/tmp/cnt2', 'wb'))
    input("Press Enter to continue...")
    cnt3 = make_cnt(3)
    pickle.dump(cnt3, open('/tmp/cnt3', 'wb'))
    input("Press Enter to continue...")
    cnt4 = make_cnt(4)
    pickle.dump(cnt4, open('/tmp/cnt4', 'wb'))
    input("Press Enter to continue...")
    ```
    这个代码主要是观察 Counter 的内存占用情况,观察结果是:
    1. 一个 600 万条的 Counter 占用 900M 内存,平均每条记录占用 150 个字节,5 个这样的 Counter 总计占用内存 4.5G (个人觉得并不算很过分)
    2. pickle 序列化后得到的文件是 130 多 M


    ```
    import collections
    import pickle

    if __name__ == '__main__':
    cnt0 = pickle.load(open('/tmp/cnt0', 'rb'))
    input("Press Enter to continue...")
    cnt1 = pickle.load(open('/tmp/cnt1', 'rb'))
    input("Press Enter to continue...")
    cnt2 = pickle.load(open('/tmp/cnt2', 'rb'))
    input("Press Enter to continue...")
    cnt3 = pickle.load(open('/tmp/cnt3', 'rb'))
    input("Press Enter to continue...")
    cnt4 = pickle.load(open('/tmp/cnt4', 'rb'))
    input("Press Enter to continue...")
    ```
    这个代码主要是观察 pickle 反序列化过程中会不会产生额外的内存占用,以及反序列化出来的对象实际大小如何,观察结果是:
    1. pickle 的反序列化过程有轻微的内存使用上涨但并不会产生内存泄漏
    2. 反序列化出来的 Counter 略小于直接动态生成的 Counter,5 个 Counter 总计占用内存 4.3G


    总结一下:
    1. pickle 这个库挺不错的,压缩率和运算性能都很可观,而且也没观察到内存爆增或者内存泄漏的情况(至少在我的测试环境下是如此的)
    2. 楼主所用的 Counter 每条记录占用 150 字节左右,总计应该是需要 4.5G 左右的内存,6G 内存应该是能撑得住的
    3. 我猜楼主在后续的合并操作没有及时回收内存,故而需求了几乎翻倍的内存
    sunhk25
        60
    sunhk25  
       2018-09-25 17:51:52 +08:00 via Android
    hiths
        61
    hiths  
       2018-09-25 18:16:40 +08:00 via iPhone
    楼主战斗力好强啊,顺便快来看一下楼上的答案吧。
    skinny
        62
    skinny  
    OP
       2018-09-25 19:23:53 +08:00
    @wutiantong

    你也可以完全不用 pickle 来测试,测试数据你可以简单先生成 3000 万条长度为 3-32 的 ascii printable 字符串(我这最长的一条有 95 个字节,不过非常非常少,多是 6 到 12 个字节的),重复数据条数占 24%,重复数据你随机插入,生成的字符串作为 K,V 你可以随机产生 1 到 2000 万之间的值,,生成测试数据要花时间,你也可以用更好的办法。

    说实在的,我一开始就是吐槽 Python 对象内存占用大。

    为了避免有人硬说内存泄露,你可以用如下代码(结果一样,内存需要非常多):

    ```python

    # Version: Python 3.6.6 x64

    import collections
    import glob

    counter = collections.Counter()
    # 你也可以直接用 dict
    # counter = dict()

    for path in glob.glob(r'F:\TestData\*.txt'):
    with open(path, encoding='UTF-8') as input_stream:
    for line in input_stream:
    tmp = line.rstrip()
    k, v = tmp.rsplit('\t', maxsplit=1)
    c = int(v)
    del v
    del tmp

    counter[k] += c

    # n = counter.get(k, 0)
    # counter[k] = c + n
    ```

    我用.net core 按相似的方法实现了一遍,速度很快,内存占用 3GB:

    ```c#
    // .net core 2.1
    //
    // Program.cs build options: Release, x64

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;

    namespace db_conv_cs
    {
    class Program
    {
    static void Main(string[] args)
    {
    Dictionary<string, uint> counter = new Dictionary<string, uint>();

    string[] files = new string[]
    {
    @"F:\TestData\dat-1.txt",
    @"F:\TestData\dat-2.txt",
    @"F:\TestData\dat-3.txt",
    @"F:\TestData\dat-4.txt",
    @"F:\TestData\dat-5.txt"
    };

    foreach (string path in files)
    {
    InsertItems(path, counter);
    }

    Console.WriteLine("{0} items", counter.Count);
    }

    static void InsertItems(string path, Dictionary<string, uint> counter)
    {
    FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read);
    StreamReader reader = new StreamReader(file, Encoding.UTF8);
    string line = null;

    while ((line = reader.ReadLine()) != null)
    {
    line = line.TrimEnd('\r', '\n');

    int i = line.LastIndexOf('\t');

    if (i == 0)
    {
    continue;
    }

    string k = line.Substring(0, i);
    string v = line.Substring(i + 1);
    uint n = uint.Parse(v);

    uint c = 0;
    counter.TryGetValue(k, out c);
    counter[k] = c + n;
    }

    reader.Close();
    file.Close();
    }
    }
    }
    ```
    skinny
        63
    skinny  
    OP
       2018-09-25 19:29:43 +08:00
    自己立一个靶子来攻击,然后我反驳就说我是杠精,也没谁了,你们爱怎么看怎么看吧。

    最后:
    https://stackoverflow.com/a/30316760
    hotsymbol
        64
    hotsymbol  
       2018-09-26 00:17:34 +08:00
    Golang 了解一下?
    lolizeppelin
        65
    lolizeppelin  
       2018-09-26 00:22:47 +08:00 via Android
    python int 最少 28 字节 能不大么……
    slixurd
        66
    slixurd  
       2018-09-26 00:35:35 +08:00   ❤️ 1
    本来 Python 的内存模型就设计成这样
    内存占用大的飞起,比 Java 还大的多
    这有什么好争的,看下对象的格式不就知道了....
    zwh2698
        67
    zwh2698  
       2018-09-26 05:05:30 +08:00 via Android
    在 @dychenyi 兄弟基础上给你补一句,windows 上叫 creatfilemapping,只要你愿意,TB 级别的都可以。
    petelin
        68
    petelin  
       2018-09-26 07:24:49 +08:00 via Android
    不是有人硬要说什么什么,是你的杆精。你说没泄露你程序就没泄露了?
    人家给出来自己测试数据了,160MB 到内存里 4-5G,多清晰。你不能找着跑一遍看有没有问题?还逼逼了一堆不知道啥玩意,另外你的代码是真的丑,你引入一堆中间变量然后 del 干什么,链式调用不更优雅一点?
    skinny
        69
    skinny  
    OP
       2018-09-26 07:42:00 +08:00
    @slixurd 他们一些人是在捏造论点并攻击,没有一个跟我吐槽相关(推荐 hdf5 之类优秀库的除外),你反驳就是杠精、沟通能力、理解能力差,你说到底谁杠精、理解能力差?如果我一开始不说使用场景,他们要喷我什么也不说就瞎说,我说了又扣了个字眼说我不会用,回他们换成别的也可以,然后又说不给真实测试数据和代码,让他们随便塞按描述生成的那么 3000 万条到内存试试就成,range 总会用吧,我都不强求你怎么样了,然后又说我表达能力差。话说我跟他们兜什么圈子,内存占用、对象大小不是明摆着的么!跟杠精争什么!我本来就是吐个槽而已。
    skinny
        70
    skinny  
    OP
       2018-09-26 07:45:02 +08:00
    @petelin 用 del 是免得和你这类杠精一样硬说有内存泄露。你要是还觉得有内存泄露你自己去写吧。那段代码怎么简单怎么来,免得优雅了你们又要说内存泄露。
    kljsandjb
        71
    kljsandjb  
       2018-09-26 08:07:15 +08:00 via iPhone
    你需要跟 c 混编
    jimisun
        72
    jimisun  
       2018-09-26 08:13:04 +08:00 via Android
    tomcat 路过
    xrui
        73
    xrui  
       2018-09-26 08:17:13 +08:00 via Android
    同感,之前整了个爬虫,想记录一下已访问过的网址。list.append (网址字符串),我看着任务管理器,每个网址大概消耗 1M 多内存。后来放弃了,写文件了
    petelin
        74
    petelin  
       2018-09-26 09:15:20 +08:00 via Android
    @skinny 人家写了啊,逻辑清晰,结果挺好的,说明够用。基本能证明就是你写的不行。
    skinny
        75
    skinny  
    OP
       2018-09-26 09:33:25 +08:00
    @petelin 你们是不是扛得精神分裂,最后开始扛自己了。我标题就说得是 Python 太占内存,主贴里也是吐槽的这个,甚至描述都可以让你们生成数据测试。你们开始硬是说什么我不会用 pickle,然后又硬说 pickle 内存泄露,那人写了个测试说 pickle 没有泄露,然后我进一步遂了你们的心,压根儿不用 pickle,再次强调了数据生成和给了新的测试代码,你们照这个试试再杠?
    est
        76
    est  
       2018-09-26 10:41:45 +08:00
    @skinny 扛 杠

    哈哈哈
    lihongjie0209
        77
    lihongjie0209  
       2018-09-26 10:50:39 +08:00
    @skinny 冒泡排序拿 C 写和拿 C#写差不了太多, 时间复杂度放在那里呢. 你要是指望拿汇编写出 O(nlgn)的冒泡排序那是你的问题. Python 对象占用空间当然比 C 大, 但是你代码设计合理吗, 你的代码完美到需要升级语言才能优化的地步? 我怀疑是你代码问题, 和 Python 没什么关系, 你又不提供代码, 又不提供测试数据, 只知道吐槽, 对你没有帮助, 对社区没有帮助.
    lihongjie0209
        78
    lihongjie0209  
       2018-09-26 10:52:04 +08:00
    @est 这个帖子很有意思
    lihongjie0209
        79
    lihongjie0209  
       2018-09-26 10:56:53 +08:00
    @xrui 你这是在开玩笑, 给代码, 给测试用例, 给 profile 然后再说一个网址可以占用 1M 内存
    est
        80
    est  
       2018-09-26 11:46:57 +08:00
    @lihongjie0209 大力出奇迹。
    xrui
        81
    xrui  
       2018-09-26 11:53:32 +08:00
    @lihongjie0209
    不知这样杠精你可满意?任务管理器表面每 5 秒 python 内存使用增加 1M

    import requests
    import time
    from lxml import etree

    urls = ["https://movie.douban.com/subject/25820460/?from=subject-page"]
    visited=[]
    for i in range(100):
    try:
    url=urls[0]
    visited.append(url)
    urls.pop(0)
    data = requests.get(url).text
    s=etree.HTML(data)
    title=s.xpath('//*[@id="content"]/h1/span[1]/text()')[0]
    nextu=s.xpath('//*[@id="recommendations"]/div/dl[5]/dd/a/@href')[0]
    urls.append(nextu)
    time.sleep(5)
    except:
    print("sth")
    else:
    print(title)
    lihongjie0209
        82
    lihongjie0209  
       2018-09-26 12:49:20 +08:00
    @xrui 我都不想说你, 自己看看代码写的什么鬼样

    只爬取前 20 条:


    types | # objects | total size
    ==================================== | =========== | ============
    <class 'str | 34273 | 4.89 MB
    <class 'dict | 4859 | 2.61 MB
    <class 'type | 1402 | 1.42 MB
    <class 'code | 9838 | 1.36 MB
    <class 'set | 1073 | 508.22 KB
    <class 'tuple | 4423 | 289.40 KB
    <class 'list | 2457 | 287.18 KB
    <class 'weakref | 2903 | 226.80 KB
    <class 'wrapper_descriptor | 2383 | 186.17 KB
    <class 'builtin_function_or_method | 2607 | 183.30 KB
    <class 'abc.ABCMeta | 146 | 145.60 KB
    <class 'getset_descriptor | 1873 | 131.70 KB
    <class 'int | 4203 | 125.74 KB
    <class 'method_descriptor | 1358 | 95.48 KB
    function (__init__) | 688 | 91.38 KB
    美国队长 3 Captain America: Civil War
    蚁人 Ant-Man
    钢铁侠 Iron Man
    X 战警 X-Men
    蜘蛛侠 Spider-Man
    我,机器人 I, Robot
    终结者 The Terminator
    变形金刚 Transformers
    我,机器人 I, Robot
    终结者 The Terminator
    变形金刚 Transformers
    我,机器人 I, Robot
    终结者 The Terminator
    变形金刚 Transformers
    我,机器人 I, Robot
    终结者 The Terminator
    变形金刚 Transformers
    我,机器人 I, Robot
    终结者 The Terminator
    变形金刚 Transformers
    types | # objects | total size
    ==================================== | =========== | ============
    <class 'str | 34427 | 5.27 MB
    <class 'dict | 4878 | 2.62 MB
    <class 'type | 1410 | 1.44 MB
    <class 'code | 9866 | 1.36 MB
    <class 'set | 1073 | 508.72 KB
    <class 'tuple | 4461 | 292.20 KB
    <class 'list | 2488 | 291.38 KB
    <class 'weakref | 2917 | 227.89 KB
    <class 'wrapper_descriptor | 2383 | 186.17 KB
    <class 'builtin_function_or_method | 2608 | 183.38 KB
    <class 'abc.ABCMeta | 146 | 147.05 KB
    <class 'getset_descriptor | 1878 | 132.05 KB
    <class 'int | 4216 | 126.14 KB
    <class 'method_descriptor | 1358 | 95.48 KB
    function (__init__) | 695 | 92.30 KB


    爬取 20 条之后 str 的内存占用增加了 0.38M, 与你的描述: 每个网址大概消耗 1M 多内存 存在较大的差异, 需要你提供你的内存占用测试以及结果


    计算内存占用的方法:

    from pympler import summary
    from pympler import muppy
    def print_mem():
    summarize = summary.summarize(muppy.get_objects())
    summary.print_(summarize)

    参考文档:
    https://pythonhosted.org/Pympler/muppy.html#muppy
    lihongjie0209
        83
    lihongjie0209  
       2018-09-26 12:50:25 +08:00
    @lihongjie0209 最开始的那句话发错了, 和本问题无关
    leemove
        85
    leemove  
       2018-09-26 13:54:27 +08:00   ❤️ 2
    小伙子敢吐槽 python 勇气可嘉.v 站只允许吐槽 js,php 辣鸡,python 是上等语言,上等语言的事能叫占用内存多吗?
    lihongjie0209
        86
    lihongjie0209  
       2018-09-26 14:10:03 +08:00
    @xrui

    print_mem()
    l = []
    for i in range(20):
    l.append("https://movie.douban.com/subject/25820460/?from=subject-page")
    # time.sleep(.5)

    print_mem()

    自己测试一下完全只 append string 的内存占用, 几乎没有



    你的内存上涨只可能是 python 的 gc 没有把 垃圾回收而已, 如果你需要准确控制垃圾回收, 那么自动 gc 的语言不适合你
    lihongjie0209
        87
    lihongjie0209  
       2018-09-26 14:10:46 +08:00
    @leemove 吐槽要有理有据, 瞎 BB 谁也会
    newghost
        88
    newghost  
       2018-09-26 16:09:38 +08:00
    Java 情何以堪
    d18
        89
    d18  
       2018-09-26 17:07:20 +08:00
    成功钓出一群杠精?
    firebroo
        90
    firebroo  
       2018-09-26 18:54:37 +08:00
    python 肯定大。。正常。https://github.com/firebroo/UnixTools/blob/master/uniq/hashtable.h 你说的粗糙的 c 的 hashmap,不能说是粗糙,这东西越精细内存占用越大,只能说精简。
    clino
        91
    clino  
       2018-09-26 21:42:44 +08:00
    楼上你们贴代码怎么都不用 gist?特别是对于 python 这种缩进严格的语言...
    jswangjieda
        92
    jswangjieda  
       2018-09-27 09:43:16 +08:00
    python 完全面向对象,包括 int,float 这种数字都是对象,内存占用更多好像也没啥奇怪的,原生 python 在性能上本来就不好,一般数据处理的话还是用 numpy 这种用 cython 写的库。拿 python 直接去和 java,c 之类的语言对比内存占用和运行速度本来就不对吧。
    skinny
        93
    skinny  
    OP
       2018-09-27 09:51:34 +08:00
    @jswangjieda 我一开始没有直接去和 java,c 之类的语言对比,我开始说的是 Python 内存占用远远超过预估,所以吐槽了下 Python 内存占用太多(就这次的经验来看,大量小数据(超千万)不适合用标准库和内置容器对象)。可是炸出来一群杠精,所以才有后面别的语言的处理代码和结果。

    @newghost Java 分配 3.5GB 内存可以搞定的,就是 CPU 使用率有点高。
    skinny
        94
    skinny  
    OP
       2018-09-27 09:55:02 +08:00
    @clino 太麻烦,且主要是代码太简单。还有吐槽下主贴有支持 markdown,回复却不支持,还要删除回复全部行首空格。
    clino
        95
    clino  
       2018-09-27 10:01:35 +08:00
    @skinny 你是没用过 gist 吗? 怎么会麻烦? 只要编辑好这里提供地址就行了
    jswangjieda
        96
    jswangjieda  
       2018-09-27 10:10:51 +08:00
    @skinny python 完全面向对象和动态类型的特性乍一看很美,但是对需要高性能的需求来说无异于是毒瘤,好在有 cython/numba 等库的支撑,不然 python 在科学计算上也不会这么好用
    skinny
        97
    skinny  
    OP
       2018-09-27 10:25:55 +08:00
    @jswangjieda 估计等下要杠你了
    jswangjieda
        98
    jswangjieda  
       2018-09-27 10:42:05 +08:00
    @skinny hahaha,没事没事,我只是说了我知道的事情,我本来就只拿 python 捣腾机器学习,其他方面又不懂,要是有人拿他们懂的东西来杠我,我也懒得回应
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1012 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 20:24 · PVG 04:24 · LAX 12:24 · JFK 15:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.