python memory usage




Pythonのサブプロセスです。 "OSError:[Errno 12]メモリを割り当てることができません" (4)

注:この質問はもともとhere尋ねられhereが、受け入れ可能な回答が実際には見つかりませんでしhereが、賞金期限が切れました。 私は元の質問に記載されているすべての詳細を含むこの質問を再請求しています。

Pythonスクリプトは、 schedモジュールを使用して60秒ごとに一連のクラス関数を実行してschedます。

# sc is a sched.scheduler instance
sc.enter(60, 1, self.doChecks, (sc, False))

スクリプトはhereコードを使用してデーモン化されたプロセスとして実行されていhere

doChecksの一部として呼び出される多くのクラスメソッドは、システム統計を取得するためにsubprocessモジュールを使用してシステム関数を呼び出します。

ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]

これは、スクリプト全体が次のエラーでクラッシュする前に一定期間実行されます。

File "/home/admin/sd-agent/checks.py", line 436, in getProcesses
File "/usr/lib/python2.4/subprocess.py", line 533, in __init__
File "/usr/lib/python2.4/subprocess.py", line 835, in _get_handles
OSError: [Errno 12] Cannot allocate memory

スクリプトがクラッシュしたあと、free -mの出力は次のようになります。

$ free -m
                  total       used       free     shared     buffers    cached
Mem:                894        345        549          0          0          0
-/+ buffers/cache:  345        549
Swap:                 0          0          0

サーバーはCentOS 5.3を実行しています。 私自身のCentOSボックスや同じ問題を報告している他のユーザーとは再現できません。

私は、元の質問に示唆されているように、これをデバッグするためにいくつかのことを試みました:

  1. Popenコールの前後にfree -mの出力を記録します。 メモリ使用量に大きな変化はありません。つまり、スクリプトの実行中にメモリが徐々に使い果たされることはありません。

  2. 私はclose_fds = PopenコールにTrueを追加しましたが、これは違いはありません - スクリプトは同じエラーでクラッシュしました。 herehere勧めしhere

  3. here提案hereように、RLIMIT_DATAとRLIMIT_ASの両方で(-1、-1)を示したリミットをチェックしhere

  4. 記事では、スワップスペースがないことが原因かもしれないと示唆しているが、スワップは実際にはオンデマンドで利用できる(ウェブホストによる)と、これはhere偽の原因として示唆された。

  5. .communicate()をPythonソースコードとコメントhereバックアップして使用する動作であるため、プロセスは終了していhere

チェックの全体はGitHubの 442行で定義されたgetProcesses関数で見つけることができます。これは520行目からdoChecks()によって呼び出されます。

スクリプトはクラッシュの前に以下の出力を伴うstraceで実行されました:

recv(4, "Total Accesses: 516662\nTotal kBy"..., 234, 0) = 234
gettimeofday({1250893252, 887805}, NULL) = 0
write(3, "2009-08-21 17:20:52,887 - checks"..., 91) = 91
gettimeofday({1250893252, 888362}, NULL) = 0
write(3, "2009-08-21 17:20:52,888 - checks"..., 74) = 74
gettimeofday({1250893252, 888897}, NULL) = 0
write(3, "2009-08-21 17:20:52,888 - checks"..., 67) = 67
gettimeofday({1250893252, 889184}, NULL) = 0
write(3, "2009-08-21 17:20:52,889 - checks"..., 81) = 81
close(4)                                = 0
gettimeofday({1250893252, 889591}, NULL) = 0
write(3, "2009-08-21 17:20:52,889 - checks"..., 63) = 63
pipe([4, 5])                            = 0
pipe([6, 7])                            = 0
fcntl64(7, F_GETFD)                     = 0
fcntl64(7, F_SETFD, FD_CLOEXEC)         = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f12708) = -1 ENOMEM (Cannot allocate memory)
write(2, "Traceback (most recent call last"..., 35) = 35
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/agent."..., 52) = 52
open("/home/admin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/home/admin/sd-agent/dae"..., 60) = 60
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/agent."..., 54) = 54
open("/usr/lib/python2.4/sched.py", O_RDONLY|O_LARGEFILE) = 8
write(2, "  File \"/usr/lib/python2.4/sched"..., 55) = 55
fstat64(8, {st_mode=S_IFREG|0644, st_size=4054, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "\"\"\"A generally useful event sche"..., 4096) = 4054
write(2, "    ", 4)                     = 4
write(2, "void = action(*argument)\n", 25) = 25
close(8)                                = 0
munmap(0xb7d28000, 4096)                = 0
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/checks"..., 60) = 60
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/checks"..., 64) = 64
open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
write(2, "  File \"/usr/lib/python2.4/subpr"..., 65) = 65
fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
read(8, "lso, the newlines attribute of t"..., 4096) = 4096
read(8, "code < 0:\n        print >>sys.st"..., 4096) = 4096
read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
read(8, " p2cread\n        # c2pread    <-"..., 4096) = 4096
write(2, "    ", 4)                     = 4
write(2, "errread, errwrite)\n", 19)    = 19
close(8)                                = 0
munmap(0xb7d28000, 4096)                = 0
open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
write(2, "  File \"/usr/lib/python2.4/subpr"..., 71) = 71
fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
read(8, "lso, the newlines attribute of t"..., 4096) = 4096
read(8, "code < 0:\n        print >>sys.st"..., 4096) = 4096
read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
read(8, " p2cread\n        # c2pread    <-"..., 4096) = 4096
read(8, "table(self, handle):\n           "..., 4096) = 4096
read(8, "rrno using _sys_errlist (or siml"..., 4096) = 4096
read(8, " p2cwrite = None, None\n         "..., 4096) = 4096
write(2, "    ", 4)                     = 4
write(2, "self.pid = os.fork()\n", 21)  = 21
close(8)                                = 0
munmap(0xb7d28000, 4096)                = 0
write(2, "OSError", 7)                  = 7
write(2, ": ", 2)                       = 2
write(2, "[Errno 12] Cannot allocate memor"..., 33) = 33
write(2, "\n", 1)                       = 1
unlink("/var/run/sd-agent.pid")         = 0
close(3)                                = 0
munmap(0xb7e0d000, 4096)                = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x589978}, {0xb89a60, [], SA_RESTORER, 0x589978}, 8) = 0
brk(0xa022000)                          = 0xa022000
exit_group(1)                           = ?

munmap(0xb7d28000,4096)= 0
write(2、 "OSError"、7)= 7

私はこのような見せかけのコードを見た:

serrno = errno;
some_Syscall(...)
if (serrno != errno)
/* sound alarm: CATROSTOPHIC ERROR !!! */

これがPythonコードで起こっているかどうかを確認する必要があります。 Errnoは、進行中のシステムコールが失敗した場合にのみ有効です。

追加するように編集:

このプロセスがどれくらいの期間続くかは言わない。 メモリーの消費者

  • フォークされたプロセス
  • 未使用のデータ構造
  • 共有ライブラリ
  • メモリマップファイル

free -mの出力を見ると、実際にはスワップ・メモリーを使用できないようです。 Linuxでは、スワップは常にオンデマンドで自動的に利用できるのかどうかはわかりませんが、私は同じ問題を抱えていました。 しかし、いくつかのスワップメモリ​​を追加することで、私の場合はこの問題を解決しました。これは他の人が同じ問題に直面するのを助けるかもしれないので、私はUbuntu 12.04で1GBスワップを追加する方法について私の答えを投稿します。

スワップメモリ​​が有効になっているかどうか最初に確認できます。

$sudo swapon -s

空の場合は、スワップを有効にしていないことを意味します。 1GBスワップを追加するには:

$sudo dd if=/dev/zero of=/swapfile bs=1024 count=1024k
$sudo mkswap /swapfile
$sudo swapon /swapfile

fstabに次の行を追加して、スワップを永続的にします。

$sudo vim /etc/fstab

     /swapfile       none    swap    sw      0       0 

ソースと詳細はhere


スワップは以前に提案された赤いニシンではないかもしれません。 ENOMEM直前のpythonプロセスはどれくらいの大きさですか?

カーネル2.6では、 /proc/sys/vm/swappinessは、カーネルがどれほど積極的にスワップするかを制御し、カーネルがどのくらい、どれくらい正確にメモリをウィンクとノードで割り当てるかをovercommit*します。 あなたのFacebookの関係の状態と同様、 それは複雑です。

...しかし、スワップは実際にはオンデマンドで利用可能です(ウェブホストによると...)

free(1)コマンドの出力には依存しません。これは、サーバーインスタンスによって認識されるスワップ領域がないことを示します。 さて、あなたのウェブホストは、私がこのトピックについて私が知る以上のものを確かに持っているかもしれませんが、私が使ったバーチャルRHEL / CentOSシステムは、ゲストOSに利用可能なスワップを報告しています。

Red Hatの改訂KB Article 15252

Red Hat Enterprise Linux 5システムは、匿名メモリとシステムV共有メモリの合計がRAMの約3/4未満であれば、スワップ領域なしで正常に動作します。 .... 4GB以上のRAMを搭載したシステムで 、最低2GBのスワップ領域が必要です。

あなたの/proc/sys/vm設定を普通のCentOS 5.3インストールと比較してください。 スワップファイルを追加します。 swappinessを下げて、あなたがもう生きているかどうかを確認してください。


一般的なルール(バニラカーネルなど)では、 ENOMEM fork / clone失敗は、神のメモリ不足状態dup_mmdup_task_structalloc_pidmpol_dupmm_initなどのmpol_dupに対してmm_init理由か、 security_vm_enough_memory_mm オーバーコミットポリシーenforcingしている間にあなたに失敗しました

まず、フォークの試行時にフォークできなかったプロセスのvmsizeを確認し、オーバーコミットポリシーに関連する空きメモリ量(物理およびスワップ)と比較します(数字のプラグイン)。

特定のケースでは、Virtuozzoにはオーバーコミットの強制追加されています 。 さらに、あなたのコンテナからスワップやオーバーコミットの設定 (実際の結果に影響を与えるために)が本当に持っているコントロールがわかりません。

今、実際に前進するために、私はあなたに2つの選択肢残っていると言いたいと思います

  • より大きなインスタンスに切り替える、または
  • スクリプトのメモリー占有量をより効果的に制御するためのコーディング作業を行う

あなたがあなたではないことが判明した場合、コーディングの努力は全く役に立たないかもしれませんが、あなたがアモックを実行しているのと同じサーバー上の別のインスタンスに一緒に配置された何人かの人。

メモリ的には、 subprocess.Popenfork / clone使用していることを既に知っています。つまり、あなたが呼び出すたびにPythonが既に摂取しているメモリ 、つまり数百MBの追加メモリを要求しています。すべてfreepsような実行可能な10kBの実行ファイルを実行するために。 好ましくないオーバーコミットポリシーの場合、すぐにENOMEMます。

この親ページテーブルなどのコピーの問題を持たないfork代わりに、 vforkposix_spawnます。 しかし、 subprocess.Popenチャンクを書き直す気がしない場合は、 vfork / posix_spawn観点からposix_spawnスクリプトの先頭(Pythonのメモリフットプリントが最小のとき)で1回だけ使用して、 free / ps / sleepを実行し、ループ内でスクリプトと平行して実行します。 スクリプトの出力をポーリングするか、非同期的に処理する他のものがあれば、別のスレッドから同期的に読み込みます。データをPythonで処理しますが、従属プロセスにフォークする必要があります。

しかし 、あなたの特別なケースでは、 psを呼び出すことをスキップして、完全にfreeすることができます。 その情報は 、自分で、あるいは既存のライブラリやパッケージを介してアクセスするかどうかにかかわらずprocfsから直接Pythonで利用でき ます 。 あなたが実行していた唯一のユーティリティがpsfreeだった場合subprocess.Popensubprocess.Popenことができます完全にsubprocess.Popenください

最後に、あなたがsubprocess.Popenとして行うことは何でもsubprocess.Popenは、あなたのスクリプトがメモリをリークした場合、最終的には壁に突き当たります。 それに気をつけて、 メモリリークチェックしてください





memory