当用Python处理一个巨大的CSV文件时,如果突然停止,'killed'是什么意思?

153

我有一个Python脚本,它导入一个大型CSV文件,然后计算文件中每个单词出现的次数,最后将这些计数导出到另一个CSV文件。

但是问题在于,一旦计算完成并开始导出,终端会显示Killed

我不认为这是内存问题(如果是内存错误,我应该会得到一个内存错误而不是Killed)。

可能是进程运行时间太长了?如果是这样的话,有没有办法延长超时时间,以避免出现这种情况?

以下是代码:

csv.field_size_limit(sys.maxsize)
    counter={}
    with open("/home/alex/Documents/version2/cooccur_list.csv",'rb') as file_name:
        reader=csv.reader(file_name)
        for row in reader:
            if len(row)>1:
                pair=row[0]+' '+row[1]
                if pair in counter:
                    counter[pair]+=1
                else:
                    counter[pair]=1
    print 'finished counting'
    writer = csv.writer(open('/home/alex/Documents/version2/dict.csv', 'wb'))
    for key, value in counter.items():
        writer.writerow([key, value])

finished counting打印之后发生了Killed,完整的消息是:

killed (program exited with code: 137)

7
请提供您所收到的错误信息的确切文本。 - Robert Harvey
2
“killed” 通常意味着进程接收到某个信号导致其退出。在这种情况下,由于它发生在脚本的同时,很有可能是一个破损的管道,该进程正在尝试从或向已在另一端关闭的文件句柄读取或写入数据。 - Andrew Clark
4
这不是关于“killed”消息来自哪里的答案,但如果它是由于超过某种系统内存限制引起的,您可以尝试在最终循环中使用counter.iteritems()代替counter.items()来解决该问题。在Python 2中,items返回字典中键和值的列表,如果非常大可能需要大量内存。相比之下,iteritems是一个生成器,每次只需要一小部分内存。 - Blckknght
6个回答

147

退出码137(128+9)表示您的程序因接收到信号9而退出,该信号为SIGKILL。这也解释了killed消息。问题是,为什么会收到该信号?

最可能的原因是您的进程越过了允许使用的系统资源限制。根据您的操作系统和配置,这可能意味着您打开了太多文件、使用了太多文件系统空间或其他一些原因。最有可能的是,您的程序使用了太多内存。为了避免内存分配失败导致事情出错,系统向使用过多内存的进程发送kill信号。

正如我之前评论的那样,在打印finished counting后遇到内存限制的原因之一是您在最后一个循环中对counter.items()的调用会分配一个包含字典中所有键和值的列表。如果您的字典数据很多,这可能是一个非常大的列表。一个可能的解决方案是使用counter.iteritems(),它是一个生成器。它不会返回所有项目的列表,而是让您以更少的内存使用率迭代它们。

因此,我建议您尝试使用以下代码作为最后一个循环:

for key, value in counter.iteritems():
    writer.writerow([key, value])

请注意,在Python 3中,items返回一个"dictionary view"对象,它不像Python 2的版本那样具有相同的开销。它替代了iteritems,因此如果您以后升级Python版本,您将最终改变循环回到原来的方式。


3
正确,但字典本身也会占用大量内存。原帖作者应该考虑逐步读取和处理文件,而不是一次性全部读入。 - Kevin

36

涉及两个存储区域: 栈和堆。栈是保存方法调用当前状态的地方(即局部变量和引用),堆是存储对象的地方。递归和内存

我猜在counter字典中有太多键会消耗堆区域过多的内存,因此Python运行时会引发OutOfMemory异常。

为了节省内存,请不要创建巨大的对象,例如counter

1.栈溢出

一个创建太多局部变量的程序。

Python 2.7.9 (default, Mar  1 2015, 12:57:24) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open('stack_overflow.py','w')
>>> f.write('def foo():\n')
>>> for x in xrange(10000000):
...   f.write('\tx%d = %d\n' % (x, x))
... 
>>> f.write('foo()')
>>> f.close()
>>> execfile('stack_overflow.py')
Killed

2.OutOfMemory

创建一个包含太多键的巨大dict的程序。

>>> f = open('out_of_memory.py','w')
>>> f.write('def foo():\n')
>>> f.write('\tcounter = {}\n')
>>> for x in xrange(10000000):
...   f.write('counter[%d] = %d\n' % (x, x))
... 
>>> f.write('foo()\n')
>>> f.close()
>>> execfile('out_of_memory.py')
Killed

参考资料


7

很可能是您的内存不足,因此内核终止了您的进程。

您听说过 OOM Killer 吗?

以下是我为处理大量CSV文件开发的脚本的日志:

Mar 12 18:20:38 server.com kernel: [63802.396693] Out of memory: Kill process 12216 (python3) score 915 or sacrifice child
Mar 12 18:20:38 server.com kernel: [63802.402542] Killed process 12216 (python3) total-vm:9695784kB, anon-rss:7623168kB, file-rss:4kB, shmem-rss:0kB
Mar 12 18:20:38 server.com kernel: [63803.002121] oom_reaper: reaped process 12216 (python3), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

这段内容是从/var/log/syslog中获取的。

简单来说:

PID 12216被选为牺牲者(由于它使用了+9Gb的total-vm),所以oom_killer终止了它。

这里有一篇关于OOM行为的文章。


1
+1,只是想澄清一下,为了了解我的程序正在尝试使用多少RAM,我应该将total-vm、anon-rss、file-rss这些值相加吗?另外,total-vm给出的是我的程序正在使用的内存量,而不是实际可用内存量,对吗?抱歉,知识有限。 - momo
1
我的知识在这个领域也是有限的,@momo。我现在时间有点紧,无法进行更深入的调查,但我找到了这篇文章,可能会有所帮助:https://dev59.com/ZGMk5IYBdhLWcg3w7CKX 。我能告诉你的是,total-vm确实是进程使用的内存量。 - ivanleoncz

5

我刚在新的Ubuntu 20.04 LTS中尝试从VirtualBox的共享文件夹运行一个Python脚本时,遇到了同样的问题。在加载我的个人库时,Python会因为Killed而失败。当我将文件夹移动到本地目录时,问题消失了。似乎Killed是发生在导入我的库之初,因为一旦我把文件夹移到本地,我就收到了有关缺少库的消息。

重新启动电脑后,问题解决了。

因此,如果程序在某些共享情况下出现问题,人们可能希望尝试将其移动到本地目录,或者这可能是一个需要操作系统重新启动的暂时性问题。


等一下,你是要重启你的主机还是虚拟机? - cglacet
1
我重新启动了虚拟机。问题出现在我正在构建一个新的虚拟机,并且刚刚安装了Python。重新启动虚拟机后,问题消失了。我很讨厌通过重启来解决问题,所以花了很多时间尝试调试,在挖掘了一个小时后,我放弃了并进行了重启。 - Timothy C. Quinn

5

我怀疑仅仅因为时间过长就会导致进程被杀死。"被杀死"通常意味着外部因素终止了进程,但在这种情况下,可能不是因为按下Ctrl-C导致Python退出并引发KeyboardInterrupt异常。此外,在Python中,如果出现内存问题,你将得到MemoryError异常。可能正在发生的是,你遇到了Python或标准库代码中的错误,导致进程崩溃。


一个崩溃的错误很可能会导致段错误,而不是被"SIGKILL"信号终止,除非Python代码中某个地方出现了raise(SIGKILL) - Kevin
1
Python中的一个错误不会发送SIGKILL。 - qwr

0
我遇到了类似的问题,进程以代码137退出,但我运行的脚本并没有消耗太多内存以致于被OOM杀掉。结果发现我使用的Python解释器已经损坏,所以不得不彻底删除该安装并使用一个新的解释器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接