Python程序占用过多内存

6
我编写了一个小程序,使用MinimalModbus在串口上收集数据。数据将被转储到CSV文件中。我已经阅读了SO和其他地方的几篇文章。一些提到的事情是:
1. 尽可能使用惰性求值(xrange而不是range) 2. 删除未使用的大对象 3. 使用子进程,当它们死亡时,操作系统会释放内存
该脚本可在github上找到此处。我还使用脚本定期上传这些文件到服务器。这两个脚本都相当简单。此外,系统上没有运行其他任何程序,因此我认为只有内存占用问题。
如何解决这个问题才是最好的方式?我并不愿意采用子进程。
一些更多的信息:
1. 数据收集在树莓派上进行(512 MB RAM) 2. Python版本:2.7 3. 大约需要3-4天内存就会完全使用,之后树莓派就会冻结
我遵循了这篇指南,找出占用RAM最多的前20个程序。
$ ps aux | awk '{print $2, $4, $11}' | sort -k2rn | head -n 20
12434 2.2 python
12338 1.2 python
2578 0.8 /usr/sbin/console-kit-daemon
30259 0.7 sshd:
30283 0.7 -bash
1772 0.6 /usr/sbin/rsyslogd
2645 0.6 /usr/lib/policykit-1/polkitd
2146 0.5 dhclient
1911 0.4 /usr/sbin/ntpd
12337 0.3 sudo
12433 0.3 sudo
1981 0.3 sudo
30280 0.3 sshd:
154 0.2 udevd
16994 0.2 /usr/sbin/sshd
17006 0.2 ps
1875 0.2 /usr/bin/dbus-daemon
278 0.2 udevd
290 0.2 udevd
1 0.1 init

因此,这两个Python进程占用了一些RAM,但与总RAM消耗相比非常小。以下是free命令的输出。

pi@raspberrypi ~ $ free -m
             total       used       free     shared    buffers     cached
Mem:           438        414         23          0         45        320
-/+ buffers/cache:         48        389
Swap:           99          0         99

以下是top命令的输出结果。
Tasks:  69 total,   1 running,  68 sleeping,   0 stopped,   0 zombie
%Cpu(s): 66.9 us,  5.0 sy,  0.0 ni, 18.1 id,  0.0 wa,  0.0 hi, 10.0 si,  0.0 st
KiB Mem:    448776 total,   429160 used,    19616 free,    47016 buffers
KiB Swap:   102396 total,        0 used,   102396 free,   332288 cached

PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND           
12338 root    20   0 10156 5644 2384 S  69.8  1.3   3059:31 python            
26039 root    20   0     0    0    0 S   1.6  0.0   0:02.71 kworker/0:1       
26863 pi      20   0  4664 1356 1028 R   1.3  0.3   0:00.12 top               
1982 root     20   0  1752  516  448 S   0.3  0.1   1:08.36 sh                
1985 root     20   0  1692  552  460 S   0.3  0.1   5:15.16 startpar          
1 root        20   0  2144  728  620 S   0.0  0.2   0:17.43 init              
2 root        20   0     0    0    0 S   0.0  0.0   0:00.14 kthreadd          
3 root        20   0     0    0    0 S   0.0  0.0   0:13.20 ksoftirqd/0       
5 root         0 -20     0    0    0 S   0.0  0.0   0:00.00 kworker/0:0H      
7 root         0 -20     0    0    0 S   0.0  0.0   0:00.00 kworker/u:0H      
8 root         0 -20     0    0    0 S   0.0  0.0   0:00.00 khelper           
9 root        20   0     0    0    0 S   0.0  0.0   0:00.00 kdevtmpfs         
10 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 netns             
12 root      20   0     0    0    0 S   0.0  0.0   0:00.06 bdi-default       
13 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 kblockd 

编辑2

如第一个答案所建议的那样,我决定查看日志文件。我查看了syslog文件,并在其末尾得到了以下结果。

May 19 10:03:26 raspberrypi wpa_supplicant[7065]: wlan0: Failed to initialize driver    interface
May 19 10:03:49 raspberrypi wpa_supplicant[7157]: nl80211: 'nl80211' generic netlink not found
May 19 10:03:49 raspberrypi wpa_supplicant[7157]: Failed to initialize driver 'nl80211'
May 19 10:03:49 raspberrypi wpa_supplicant[7157]: rfkill: Cannot open RFKILL control device
May 19 10:03:49 raspberrypi wpa_supplicant[7157]: Could not read interface wlan0 flags: No such device

这些消息正在填充日志文件,每秒钟都会出现。有趣的是,我正在使用以太网而不是WiFi。

因此,现在不清楚RAM去了哪里?


使用软件包管理器从您的系统中删除wpa_supplicant。 - Chris Warrick
3个回答

7
大多数RAM都用于缓存和缓冲区,因此可以为应用程序提供充足的空间。查看“-/+ buffers/cache:”行以了解实际使用/空闲的RAM量。可以在这里找到解释。
要验证Python是否存在内存泄漏,请随时监视Python的RSS大小(或%mem)。例如,编写一个shell脚本,每隔几个小时从cron作业调用它,将您的ps命令链和free命令的输出追加到文件中。
如果发现Python进程确实存在内存泄漏,则有几件事情可以做;
  • 修改你的脚本,让它在24小时后退出,并使用例如cron job来重新启动它(这是简单的方法)。
  • 深入研究Python本身,特别是你正在使用的扩展模块。使用gc模块监视和影响内存使用情况。例如,您可以定期调用gc.count()来监视标记为收集的对象数量。您可以显式地调用gc.collect()并查看是否减少了内存使用量。您还可以修改收集阈值。

如果Python的RAM使用量不会随着时间的推移而增加,则可能是另一个程序或守护程序。我上面提到的内存日志记录脚本应该会告诉你是哪个。

您的计算机死机也可能有其他原因。查看Linux日志文件以获取线索。

编辑:由于你的wpa_supplicant填满了日志文件,所以你应该检查文件系统的状态。满的文件系统可能会导致系统挂起。如果您不使用无线接口,请将其禁用。


这是一个合理的解释。但为什么它会随着时间的推移不断增加呢?我该如何确定它不会导致系统冻结(这在3-4天内发生)? - Nipun Batra
@NipunBatra 你需要更仔细地查看系统。请参见更新的答案。 - Roland Smith
谢谢,我已经开始深入查看日志了。添加了EDIT 2以突出一些有趣的内容,可能与问题有关。同时按照您的建议编写脚本进行监控。 - Nipun Batra

0
也许有点晚了,但我在一个Droplet Ubuntu服务器上遇到了同样的问题。我有一个cron job每15分钟运行一次Python脚本。三天后,我的内存用完了。所以我决定通过cron每x分钟杀死Python进程,因为我没有找到解决办法。
我使用了以下命令:

pkill -f scriptName.py


0
import gc
gc.collect()

对我有用。这是从Roland Smith的被接受的答案中提取出来的,我认为它对于未来的搜索者来说是一个有用的答案。


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