16

元のタイトル:Pythonで128KB未満のファイルを開くメモリリーク?

元の質問

Pythonスクリプトを実行すると、メモリリークだと思います。これが私のスクリプトです:

import sys
import time


class MyObj(object):
    def __init__(self, filename):
        with open(filename) as f:
            self.att = f.read()


def myfunc(filename):
    mylist = [MyObj(filename) for x in xrange(100)]
    len(mylist)
    return []


def main():
    filename = sys.argv[1]
    myfunc(filename)
    time.sleep(3600)


if __name__ == '__main__':
    main()

myfunc()それぞれがファイルを開いて読み取る100個のオブジェクトのリストを作成するmain関数呼び出し。から戻った後myfunc()、100アイテムのリストからのメモリと、ファイルの読み取りからのメモリは、参照されなくなったため、解放されると思います。ただし、コマンドを使用してメモリ使用量を確認するpsと、Pythonプロセスは12行目と13行目がコメント化されたスクリプトから実行されるPythonプロセスよりも約10,000KB多くのメモリを使用します。

奇妙なことに、メモリリーク(それがそうである場合)は、サイズが128KB未満のファイルでのみ発生するようです。1KBから200KBのサイズのファイルでこのスクリプトを実行するbashスクリプトを作成し、ファイルサイズが128KBに達するとメモリの増加が停止しました。これがbashスクリプトです:

#!/bin/bash

echo "PID RSS S TTY TIME COMMAND" > output.txt

for i in `seq 1 200`;
do
    python debug_memory.py "data/stuff_${i}K.txt" &
    pid=$!
    sleep 0.1
    ps -e -O rss | grep $pid | grep -v grep >> output.txt
    kill $pid
done   

bashスクリプトの出力は次のとおりです。

PID RSS S TTY TIME COMMAND
28471  5552 S pts/16   00:00:00 python debug_memory.py data/stuff_1K.txt
28477  5656 S pts/16   00:00:00 python debug_memory.py data/stuff_2K.txt
28483  5756 S pts/16   00:00:00 python debug_memory.py data/stuff_3K.txt
28488  5852 S pts/16   00:00:00 python debug_memory.py data/stuff_4K.txt
28494  5952 S pts/16   00:00:00 python debug_memory.py data/stuff_5K.txt
28499  6052 S pts/16   00:00:00 python debug_memory.py data/stuff_6K.txt
28505  6156 S pts/16   00:00:00 python debug_memory.py data/stuff_7K.txt
28511  6256 S pts/16   00:00:00 python debug_memory.py data/stuff_8K.txt
28516  6356 S pts/16   00:00:00 python debug_memory.py data/stuff_9K.txt
28522  6452 S pts/16   00:00:00 python debug_memory.py data/stuff_10K.txt
28527  6552 S pts/16   00:00:00 python debug_memory.py data/stuff_11K.txt
28533  6656 S pts/16   00:00:00 python debug_memory.py data/stuff_12K.txt
28539  6756 S pts/16   00:00:00 python debug_memory.py data/stuff_13K.txt
28544  6852 S pts/16   00:00:00 python debug_memory.py data/stuff_14K.txt
28550  6952 S pts/16   00:00:00 python debug_memory.py data/stuff_15K.txt
28555  7056 S pts/16   00:00:00 python debug_memory.py data/stuff_16K.txt
28561  7156 S pts/16   00:00:00 python debug_memory.py data/stuff_17K.txt
28567  7252 S pts/16   00:00:00 python debug_memory.py data/stuff_18K.txt
28572  7356 S pts/16   00:00:00 python debug_memory.py data/stuff_19K.txt
28578  7452 S pts/16   00:00:00 python debug_memory.py data/stuff_20K.txt
28584  7556 S pts/16   00:00:00 python debug_memory.py data/stuff_21K.txt
28589  7652 S pts/16   00:00:00 python debug_memory.py data/stuff_22K.txt
28595  7756 S pts/16   00:00:00 python debug_memory.py data/stuff_23K.txt
28600  7852 S pts/16   00:00:00 python debug_memory.py data/stuff_24K.txt
28606  7952 S pts/16   00:00:00 python debug_memory.py data/stuff_25K.txt
28612  8052 S pts/16   00:00:00 python debug_memory.py data/stuff_26K.txt
28617  8152 S pts/16   00:00:00 python debug_memory.py data/stuff_27K.txt
28623  8252 S pts/16   00:00:00 python debug_memory.py data/stuff_28K.txt
28629  8356 S pts/16   00:00:00 python debug_memory.py data/stuff_29K.txt
28634  8452 S pts/16   00:00:00 python debug_memory.py data/stuff_30K.txt
28640  8556 S pts/16   00:00:00 python debug_memory.py data/stuff_31K.txt
28645  8656 S pts/16   00:00:00 python debug_memory.py data/stuff_32K.txt
28651  8756 S pts/16   00:00:00 python debug_memory.py data/stuff_33K.txt
28657  8856 S pts/16   00:00:00 python debug_memory.py data/stuff_34K.txt
28662  8956 S pts/16   00:00:00 python debug_memory.py data/stuff_35K.txt
28668  9056 S pts/16   00:00:00 python debug_memory.py data/stuff_36K.txt
28674  9156 S pts/16   00:00:00 python debug_memory.py data/stuff_37K.txt
28679  9256 S pts/16   00:00:00 python debug_memory.py data/stuff_38K.txt
28685  9352 S pts/16   00:00:00 python debug_memory.py data/stuff_39K.txt
28691  9452 S pts/16   00:00:00 python debug_memory.py data/stuff_40K.txt
28696  9552 S pts/16   00:00:00 python debug_memory.py data/stuff_41K.txt
28702  9656 S pts/16   00:00:00 python debug_memory.py data/stuff_42K.txt
28707  9756 S pts/16   00:00:00 python debug_memory.py data/stuff_43K.txt
28713  9852 S pts/16   00:00:00 python debug_memory.py data/stuff_44K.txt
28719  9952 S pts/16   00:00:00 python debug_memory.py data/stuff_45K.txt
28724 10052 S pts/16   00:00:00 python debug_memory.py data/stuff_46K.txt
28730 10156 S pts/16   00:00:00 python debug_memory.py data/stuff_47K.txt
28739 10256 S pts/16   00:00:00 python debug_memory.py data/stuff_48K.txt
28746 10352 S pts/16   00:00:00 python debug_memory.py data/stuff_49K.txt
28752 10452 S pts/16   00:00:00 python debug_memory.py data/stuff_50K.txt
28757 10556 S pts/16   00:00:00 python debug_memory.py data/stuff_51K.txt
28763 10656 S pts/16   00:00:00 python debug_memory.py data/stuff_52K.txt
28769 10752 S pts/16   00:00:00 python debug_memory.py data/stuff_53K.txt
28774 10852 S pts/16   00:00:00 python debug_memory.py data/stuff_54K.txt
28780 10952 S pts/16   00:00:00 python debug_memory.py data/stuff_55K.txt
28786 11052 S pts/16   00:00:00 python debug_memory.py data/stuff_56K.txt
28791 11152 S pts/16   00:00:00 python debug_memory.py data/stuff_57K.txt
28797 11256 S pts/16   00:00:00 python debug_memory.py data/stuff_58K.txt
28802 11356 S pts/16   00:00:00 python debug_memory.py data/stuff_59K.txt
28808 11452 S pts/16   00:00:00 python debug_memory.py data/stuff_60K.txt
28814 11556 S pts/16   00:00:00 python debug_memory.py data/stuff_61K.txt
28819 11656 S pts/16   00:00:00 python debug_memory.py data/stuff_62K.txt
28825 11752 S pts/16   00:00:00 python debug_memory.py data/stuff_63K.txt
28831 11852 S pts/16   00:00:00 python debug_memory.py data/stuff_64K.txt
28836 11956 S pts/16   00:00:00 python debug_memory.py data/stuff_65K.txt
28842 12052 S pts/16   00:00:00 python debug_memory.py data/stuff_66K.txt
28847 12152 S pts/16   00:00:00 python debug_memory.py data/stuff_67K.txt
28853 12256 S pts/16   00:00:00 python debug_memory.py data/stuff_68K.txt
28859 12356 S pts/16   00:00:00 python debug_memory.py data/stuff_69K.txt
28864 12452 S pts/16   00:00:00 python debug_memory.py data/stuff_70K.txt
28871 12556 S pts/16   00:00:00 python debug_memory.py data/stuff_71K.txt
28877 12652 S pts/16   00:00:00 python debug_memory.py data/stuff_72K.txt
28883 12756 S pts/16   00:00:00 python debug_memory.py data/stuff_73K.txt
28889 12856 S pts/16   00:00:00 python debug_memory.py data/stuff_74K.txt
28894 12952 S pts/16   00:00:00 python debug_memory.py data/stuff_75K.txt
28900 13056 S pts/16   00:00:00 python debug_memory.py data/stuff_76K.txt
28906 13156 S pts/16   00:00:00 python debug_memory.py data/stuff_77K.txt
28911 13256 S pts/16   00:00:00 python debug_memory.py data/stuff_78K.txt
28917 13352 S pts/16   00:00:00 python debug_memory.py data/stuff_79K.txt
28922 13452 S pts/16   00:00:00 python debug_memory.py data/stuff_80K.txt
28928 13556 S pts/16   00:00:00 python debug_memory.py data/stuff_81K.txt
28934 13652 S pts/16   00:00:00 python debug_memory.py data/stuff_82K.txt
28939 13752 S pts/16   00:00:00 python debug_memory.py data/stuff_83K.txt
28945 13852 S pts/16   00:00:00 python debug_memory.py data/stuff_84K.txt
28951 13952 S pts/16   00:00:00 python debug_memory.py data/stuff_85K.txt
28956 14052 S pts/16   00:00:00 python debug_memory.py data/stuff_86K.txt
28962 14152 S pts/16   00:00:00 python debug_memory.py data/stuff_87K.txt
28967 14256 S pts/16   00:00:00 python debug_memory.py data/stuff_88K.txt
28973 14352 S pts/16   00:00:00 python debug_memory.py data/stuff_89K.txt
28979 14456 S pts/16   00:00:00 python debug_memory.py data/stuff_90K.txt
28984 14552 S pts/16   00:00:00 python debug_memory.py data/stuff_91K.txt
28990 14652 S pts/16   00:00:00 python debug_memory.py data/stuff_92K.txt
28996 14756 S pts/16   00:00:00 python debug_memory.py data/stuff_93K.txt
29001 14852 S pts/16   00:00:00 python debug_memory.py data/stuff_94K.txt
29007 14956 S pts/16   00:00:00 python debug_memory.py data/stuff_95K.txt
29012 15052 S pts/16   00:00:00 python debug_memory.py data/stuff_96K.txt
29018 15156 S pts/16   00:00:00 python debug_memory.py data/stuff_97K.txt
29024 15252 S pts/16   00:00:00 python debug_memory.py data/stuff_98K.txt
29029 15360 S pts/16   00:00:00 python debug_memory.py data/stuff_99K.txt
29035 15456 S pts/16   00:00:00 python debug_memory.py data/stuff_100K.txt
29040 15556 S pts/16   00:00:00 python debug_memory.py data/stuff_101K.txt
29046 15652 S pts/16   00:00:00 python debug_memory.py data/stuff_102K.txt
29052 15756 S pts/16   00:00:00 python debug_memory.py data/stuff_103K.txt
29057 15852 S pts/16   00:00:00 python debug_memory.py data/stuff_104K.txt
29063 15952 S pts/16   00:00:00 python debug_memory.py data/stuff_105K.txt
29069 16056 S pts/16   00:00:00 python debug_memory.py data/stuff_106K.txt
29074 16152 S pts/16   00:00:00 python debug_memory.py data/stuff_107K.txt
29080 16256 S pts/16   00:00:00 python debug_memory.py data/stuff_108K.txt
29085 16356 S pts/16   00:00:00 python debug_memory.py data/stuff_109K.txt
29091 16452 S pts/16   00:00:00 python debug_memory.py data/stuff_110K.txt
29097 16552 S pts/16   00:00:00 python debug_memory.py data/stuff_111K.txt
29102 16652 S pts/16   00:00:00 python debug_memory.py data/stuff_112K.txt
29108 16756 S pts/16   00:00:00 python debug_memory.py data/stuff_113K.txt
29113 16852 S pts/16   00:00:00 python debug_memory.py data/stuff_114K.txt
29119 16952 S pts/16   00:00:00 python debug_memory.py data/stuff_115K.txt
29125 17056 S pts/16   00:00:00 python debug_memory.py data/stuff_116K.txt
29130 17156 S pts/16   00:00:00 python debug_memory.py data/stuff_117K.txt
29136 17256 S pts/16   00:00:00 python debug_memory.py data/stuff_118K.txt
29141 17356 S pts/16   00:00:00 python debug_memory.py data/stuff_119K.txt
29147 17452 S pts/16   00:00:00 python debug_memory.py data/stuff_120K.txt
29153 17556 S pts/16   00:00:00 python debug_memory.py data/stuff_121K.txt
29158 17656 S pts/16   00:00:00 python debug_memory.py data/stuff_122K.txt
29164 17756 S pts/16   00:00:00 python debug_memory.py data/stuff_123K.txt
29170 17856 S pts/16   00:00:00 python debug_memory.py data/stuff_124K.txt
29175 17952 S pts/16   00:00:00 python debug_memory.py data/stuff_125K.txt
29181 18056 S pts/16   00:00:00 python debug_memory.py data/stuff_126K.txt
29186 18152 S pts/16   00:00:00 python debug_memory.py data/stuff_127K.txt
29192  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_128K.txt
29198  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_129K.txt
29203  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_130K.txt
29209  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_131K.txt
29215  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_132K.txt
29220  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_133K.txt
29226  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_134K.txt
29231  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_135K.txt
29237  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_136K.txt
29243  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_137K.txt
29248  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_138K.txt
29254  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_139K.txt
29260  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_140K.txt
29265  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_141K.txt
29271  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_142K.txt
29276  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_143K.txt
29282  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_144K.txt
29288  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_145K.txt
29293  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_146K.txt
29299  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_147K.txt
29305  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_148K.txt
29310  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_149K.txt
29316  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_150K.txt
29321  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_151K.txt
29327  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_152K.txt
29333  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_153K.txt
29338  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_154K.txt
29344  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_155K.txt
29349  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_156K.txt
29355  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_157K.txt
29361  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_158K.txt
29366  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_159K.txt
29372  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_160K.txt
29378  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_161K.txt
29383  5460 S pts/16   00:00:00 python debug_memory.py data/stuff_162K.txt
29389  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_163K.txt
29394  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_164K.txt
29400  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_165K.txt
29406  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_166K.txt
29411  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_167K.txt
29417  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_168K.txt
29423  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_169K.txt
29428  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_170K.txt
29434  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_171K.txt
29439  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_172K.txt
29445  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_173K.txt
29451  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_174K.txt
29456  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_175K.txt
29463  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_176K.txt
29483  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_177K.txt
29489  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_178K.txt
29496  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_179K.txt
29501  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_180K.txt
29507  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_181K.txt
29512  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_182K.txt
29518  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_183K.txt
29524  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_184K.txt
29529  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_185K.txt
29535  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_186K.txt
29541  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_187K.txt
29546  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_188K.txt
29552  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_189K.txt
29557  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_190K.txt
29563  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_191K.txt
29569  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_192K.txt
29574  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_193K.txt
29580  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_194K.txt
29586  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_195K.txt
29591  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_196K.txt
29597  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_197K.txt
29602  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_198K.txt
29608  5456 S pts/16   00:00:00 python debug_memory.py data/stuff_199K.txt
29614  5452 S pts/16   00:00:00 python debug_memory.py data/stuff_200K.txt

誰かが何が起こっているのか説明できますか?128KB未満のファイルを使用すると、メモリ使用量が増加するのはなぜですか?

私の完全なテスト環境はここにあります: https ://github.com/saltycrane/debugging-python-memory-usage/tree/50f73358c7a84a504333ce9c4071b0f3537bbc0f

Ubuntu12.04でPython2.7.3を実行しています。

更新1

この問題は、サイズが128K未満のファイルの操作に固有のものではありません。オブジェクト属性をファイルから読み取ったのと同じサイズの値に設定しても、同じ結果が得られます。更新されたコードは次のとおりです。

import sys
import time


class MyObj(object):
    def __init__(self, size_kb):
        self.att = ' ' * int(size_kb) * 1024


def myfunc(size_kb):
    mylist = [MyObj(size_kb) for x in xrange(100)]
    len(mylist)
    return []


def main():
    size_kb = sys.argv[1]
    myfunc(size_kb)
    time.sleep(3600)


if __name__ == '__main__':
    main()

このスクリプトを実行すると、同様の結果が得られます。更新されたテスト環境は次の場所にあります: https ://github.com/saltycrane/debugging-python-memory-usage/tree/59b7ff61134dfc11c4195e9201b2c1728ed4fcce

更新2

テストスクリプトをさらに簡略化するには、次のようにします。1.クラスを削除し、文字列のリストを作成するだけです。2.オブジェクトを削除して削除するために使用しmyfunc()ます。delmylist

import sys
import time

def main():
    size_kb = sys.argv[1]

    mylist = []
    for x in xrange(100):
        mystr = ' ' * int(size_kb) * 1024
        mylist.append(mystr)

    del mylist

    time.sleep(3600)

if __name__ == '__main__':
    main()

私の簡略化したスクリプトでも、元のスクリプトと同様の結果が得られます。ただし、別の文字列変数を作成しないと、メモリの増加は見られません。メモリを増加させないスクリプトは次のとおりです。

import sys
import time

def main():
    size_kb = sys.argv[1]

    mylist = []
    for x in xrange(100):
        mylist.append(' ' * int(size_kb) * 1024)

    del mylist

    time.sleep(3600)

if __name__ == '__main__':
    main()

更新されたテスト環境は次の場所にあります: https ://github.com/saltycrane/debugging-python-memory-usage/tree/423ca6a50dccbe32572a9d0dea1068ddcb06663b

さらに質問を:

  • 他の誰かが私の結果を再現できますか?
  • メモリーの増加はps予想通りですか?

何が起こっているかについてのヒント

この問題に関連していると思われる「無料リスト」に関する興味深い情報をいくつか発見しました。

最後のリンクから:

メモリ割り当てを高速化(および再利用)するために、Pythonは小さなオブジェクトに多数のリストを使用します。各リストには、同様のサイズのオブジェクトが含まれます

確かに:(サイズxの)アイテムの割り当てが解除された場合(参照がないために解放された場合)、その場所はPythonのグローバルメモリプールに返されません(さらにはシステムに返されません)が、単に空きとしてマークされ、の空きリストに追加されますサイズxのアイテム。

小さなオブジェクトのメモリが解放されない場合、避けられない結論は、金魚のように、これらの小さなオブジェクトのリストは拡大し続けるだけで、縮小することはなく、アプリケーションのメモリフットプリントは、任意の場所に割り当てられた小さなオブジェクトの最大数によって支配されるということです。点。

更新3

Update 2のコードを単純化しすぎましたdel mystr。スクリプトの最後に行を追加すると、メモリが解放されました。(https://github.com/saltycrane/debugging-python-memory-usage/blob/dd058e4774802cae7cbfca520fb835ea46b645e8/debug_memory_leaks.pyを参照してください)

問題を示すために十分に複雑になるようにスクリプトを更新しました。この問題は、次のコードにまだ存在します。最新のコード/環境はここにあります:https ://github.com/saltycrane/debugging-python-memory-usage/tree/fc0c8ce9ba621cb86b6abb93adf1b297a7c0230b

import gc
import sys
import time


def main():
    size_kb = sys.argv[1]

    mylist = []
    for x in xrange(100):
        mystr = ' ' * int(size_kb) * 1024
        mydict = {'mykey': mystr}
        mylist.append(mydict)

    del mystr
    del mydict
    del mylist

    gc.collect()

    time.sleep(3600)


if __name__ == '__main__':
    main()

また、スクリプトを実行したのは他の環境です。奇妙な結果は、クリーンなvirtualenv内から実行されていました。この場合、メモリのドロップオフは128KBではなく260KBで発生しました。https://github.com/saltycrane/debugging-python-memory-usage/tree/52fbd5d57ff45affdcd70623ddb74fa1f1ffbbc2を参照してください

環境:

  • Ubuntu 12.04 64ビット、システムPython 2.7.3:元の実行
  • Ubuntu 12.04 64ビット、ソースからコンパイルされたPython 3.3.0:同様の結果
  • Scientific Linux 6 64ビット、Python 2.6.6:同様の結果
  • virtualenvからのUbuntu12.0464ビット、Python 2.7.3:メモリドロップオフは128KBではなく260KBで発生します

その他の参考資料:

アップデート4(ほとんど解決済み)

schlenkは、メモリ使用量が128KBで低下する理由を明らかにしました。128KBは、「メモリ割り当て関数」(malloc?)がsbrkを使用してプログラムブレークを増やす代わりにmmapを使用するポイントです。興味深いことに、しきい値は環境変数を介して変更できます。環境変数をさまざまな値に設定してテストを実行したところMALLOC_MMAP_THRESHOLD_、メモリ使用量の低下がその値と一致しました。結果については、こちらをご覧ください: https ://github.com/saltycrane/debugging-python-memory-usage/blob/97d93cd165a139a6b6f96720de63a92561dd2f05/output_debug_memory_leaks.py.txt

文字列値が128KB未満の場合に、スクリプトがメモリをリークする動作が予想されるかどうかを知りたいのですが。

さらにいくつかのリンク:

注:最後の2つのリンクによると、sbrkの代わりにmmapを使用すると、パフォーマンス(速度)が低下します。

4

2 に答える 2

5

Linuxメモリアロケータのデフォルトの動作を単純にヒットする可能性があります。

基本的に、Linuxには2つの割り当て戦略があります。メモリの小さなブロック用のsbrk()と大きなブロック用のmmap()です。sbrk()で割り当てられたメモリブロックはシステムに簡単に戻すことはできませんが、mmap()ベースのメモリブロックは(ページのマップを解除するだけで)できます。

したがって、libcのmalloc()アロケータがsbrk()とmmap()を切り替えることを決定した値よりも大きいメモリブロックを割り当てると、この効果が見られます。mallopt()呼び出し、特にMMAP_THRESHOLD(http://man7.org/linux/man-pages/man3/mallopt.3.html)を参照してください。

更新 追加の質問に答えるには:はい、メモリアロケータがLinuxのlibcのように機能する場合は、その方法でメモリをリークすることが予想されます。代わりにWindowsLowFragmentationHeapを使用した場合、構成されているmallocによっては、AIXの場合と同様に、リークは発生しない可能性があります。たぶん、他のアロケータ(tcmallocなど)の1つもそのような問題を修正します。sbrk()は非常に高速ですが、メモリの断片化に問題があります。CPythonには、圧縮ガベージコレクターがないため、それについて多くのことを行うことはできませんが、単純な参照カウントがあります。

Pythonは、バッファー割り当てを減らすためのいくつかの方法を提供します。たとえば、次のブログ投稿を参照してください:http ://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol -and-memoryviews /

于 2013-03-16T17:53:37.773 に答える
3

ガベージコレクションを調べます。大きなファイルがより頻繁にガベージコレクションをトリガーしている可能性がありますが、小さなファイルは解放されていますが、集合的に一定のしきい値にとどまっています。具体的には、オブジェクトに対してgc.collect()を呼び出してから、gc.get_referrers()を呼び出して、インスタンスを保持しているものが何であるかを明らかにします。こちらのPythonドキュメントを参照してください。

http://docs.python.org/2/library/gc.html?highlight=gc#gc.get_referrers

アップデート:

この問題は、ガベージコレクション、名前空間、および参照カウントに関連しています。あなたが投稿したbashスクリプトは、ガベージコレクターの動作をかなり狭い範囲で示しています。より広い範囲を試してみると、特定の範囲が必要とするメモリ量のパターンが表示されます。たとえば、次のように、bashforループをより広い範囲に変更しますseq 0 16 2056

del mystr残っている参照を削除しているため、メモリ使用量が削減されていることに気づきました。mystr変数を次のように独自の関数に制限した場合も、同様の結果が発生する可能性があります。

def loopy():
    mylist = []
    for x in xrange(100):
        mystr = ' ' * int(size_kb) * 1024
        mydict = {x: mystr}
        mylist.append(mydict)
    return mylist

bashスクリプトを使用するのではなく、メモリプロファイラーを使用してより有用な情報を取得できると思います。Pymplerを使用したいくつかの例を次に示します。この最初のバージョンは、Update3のコードに似ています。

import gc
import sys
import time
from pympler import tracker

tr = tracker.SummaryTracker()
print 'begin:'
tr.print_diff()

size_kb = sys.argv[1]

mylist = []
mydict = {}

print 'empty list & dict:'
tr.print_diff()

for x in xrange(100):
    mystr = ' ' * int(size_kb) * 1024
    mydict = {x: mystr}
    mylist.append(mydict)

print 'after for loop:'
tr.print_diff()

del mystr
del mydict
del mylist

print 'after deleting stuff:'
tr.print_diff()

collected = gc.collect()
print 'after garbage collection (collected: %d):' % collected
tr.print_diff()

time.sleep(2)
print 'took a short nap after all that work:'
tr.print_diff()

mylist = []
print 'create an empty list for some reason:'
tr.print_diff()

そして出力:

$ python mem_test.py 256
begin:
                  types |   # objects |    total size
======================= | =========== | =============
                   list |         957 |      97.44 KB
                    str |         951 |      53.65 KB
                    int |         118 |       2.77 KB
     wrapper_descriptor |           8 |     640     B
                weakref |           3 |     264     B
      member_descriptor |           2 |     144     B
      getset_descriptor |           2 |     144     B
  function (store_info) |           1 |     120     B
                   cell |           2 |     112     B
         instancemethod |          -1 |     -80     B
       _sre.SRE_Pattern |          -2 |    -176     B
                  tuple |          -1 |    -216     B
                   dict |           2 |   -1744     B
empty list & dict:
  types |   # objects |   total size
======= | =========== | ============
   list |           2 |    168     B
    str |           2 |     97     B
    int |           1 |     24     B
after for loop:
  types |   # objects |   total size
======= | =========== | ============
    str |           1 |    256.04 KB
   list |           0 |    848     B
after deleting stuff:
  types |   # objects |      total size
======= | =========== | ===============
   list |          -1 |      -920     B
    str |          -1 |   -262181     B
after garbage collection (collected: 0):
  types |   # objects |   total size
======= | =========== | ============
took a short nap after all that work:
  types |   # objects |   total size
======= | =========== | ============
create an empty list for some reason:
  types |   # objects |   total size
======= | =========== | ============
   list |           1 |     72     B

forループの後、strクラスの合計サイズは256 KBであり、これは基本的に、渡した引数と同じです。メモリ内のmystrへの参照を明示的に削除した後、del mystr解放されます。この後、ゴミはすでに拾われているので、それ以上の削減はありませんgc.collect()

次のバージョンでは、関数を使用して、文字列に別の名前空間を作成します。

import gc
import sys
import time
from pympler import tracker

def loopy():
    mylist = []
    for x in xrange(100):
        mystr = ' ' * int(size_kb) * 1024
        mydict = {x: mystr}
        mylist.append(mydict)
    return mylist


tr = tracker.SummaryTracker()
print 'begin:'
tr.print_diff()

size_kb = sys.argv[1]

mylist = loopy()

print 'after for loop:'
tr.print_diff()

del mylist

print 'after deleting stuff:'
tr.print_diff()

collected = gc.collect()
print 'after garbage collection (collected: %d):' % collected
tr.print_diff()

time.sleep(2)
print 'took a short nap after all that work:'
tr.print_diff()

mylist = []
print 'create an empty list for some reason:'
tr.print_diff()

そして最後に、このバージョンからの出力:

$ python mem_test_2.py 256
begin:
                  types |   # objects |    total size
======================= | =========== | =============
                   list |         958 |      97.53 KB
                    str |         952 |      53.70 KB
                    int |         118 |       2.77 KB
     wrapper_descriptor |           8 |     640     B
                weakref |           3 |     264     B
      member_descriptor |           2 |     144     B
      getset_descriptor |           2 |     144     B
  function (store_info) |           1 |     120     B
                   cell |           2 |     112     B
         instancemethod |          -1 |     -80     B
       _sre.SRE_Pattern |          -2 |    -176     B
                  tuple |          -1 |    -216     B
                   dict |           2 |   -1744     B
after for loop:
  types |   # objects |   total size
======= | =========== | ============
   list |           2 |   1016     B
    str |           2 |     97     B
    int |           1 |     24     B
after deleting stuff:
  types |   # objects |   total size
======= | =========== | ============
   list |          -1 |   -920     B
after garbage collection (collected: 0):
  types |   # objects |   total size
======= | =========== | ============
took a short nap after all that work:
  types |   # objects |   total size
======= | =========== | ============
create an empty list for some reason:
  types |   # objects |   total size
======= | =========== | ============
   list |           1 |     72     B

これで、strをクリーンアップする必要はありません。この例は、関数を使用することが良いアイデアである理由を示していると思います。1つの名前空間に1つの大きなチャンクがあるコードを生成すると、ガベージコレクターがその役割を果たせなくなります。それはあなたの家に来て、物事がゴミであると仮定し始めることはありません:)物事が安全に収集できることを知っている必要があります。

そのエヴァンジョーンズのリンクは非常に興味深いところで。

于 2013-03-12T00:38:18.020 に答える