6

我编写了一个小程序来使用 MinimalModbus 通过串行端口收集数据。数据被转储到 CSV 文件中。我已经阅读了关于 SO 和其他地方的几篇文章。提到的几件事是:

  1. 尽可能使用惰性求值(xrange 代替 range)
  2. 删除大型未使用对象
  3. 使用子进程并在它们死亡时由操作系统释放内存

该脚本在github 上。我还使用脚本定期将这些文件上传到服务器。这两个脚本都很简单。系统上也没有运行其他任何东西,因此我觉得这两个系统只发生内存占用。解决这个问题的最佳方法是什么。我不是最愿意采用子流程路线的。

更多信息:

  1. 数据收集在 Raspberry Pi (512 MB RAM) 上
  2. Python版本:2.7
  3. 完全使用 RAM 大约需要 3-4 天,然后 RaspberryPi 冻结

我按照指南找出了消耗 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,以下是 tail 的结果。

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 去了哪里?

4

3 回答 3

7

您的大部分 RAM 对应用程序都是免费的,因为它用于缓冲区和缓存。查看“-/+ 缓冲区/缓存:”行以查看实际使用/空闲的 RAM 量。可以在这里找到解释。

要验证 Python 是否泄漏内存,请随时间监控该 Python 的 RSS 大小(或 %mem)。例如,编写一个每隔几个小时从 cron 作业调用的 shell 脚本,以将ps命令链的输出和命令的输出附加free到文件中。

如果你发现 Python 进程正在泄漏内存,你可以做几件事;

  • 将您的脚本修改为它在 24 小时后存在并使用例如 cron 作业重新启动它(简单的方法。)
  • 深入了解 Python 本身,尤其是您正在使用的扩展模块。使用该gc模块来监控和影响内存使用情况。例如,您可以gc.count()定期调用以监控标记为收集的对象的数量。您可以gc.collect()显式调用并查看是否会减少内存使用量。您还可以修改收集阈值。

如果 Python 的 RAM 使用不随时间增加,它可能是另一个守护程序。我上面提到的内存记录脚本应该告诉你它是哪一个。

您的计算机死机可能还有另一个原因。查看 Linux 日志文件以获取线索。

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

于 2013-05-19T09:15:12.463 回答
0
import gc
gc.collect()

为我工作。这取自 Roland Smith 接受的答案,我认为将来的搜索者指出它作为答案很有用。

于 2018-04-20T08:46:36.617 回答
0

也许为时已晚,但我在 droplet ubuntu 服务器上遇到了同样的问题。有一个 cron 作业每 15 分钟运行一次 python 脚本。三天后,我的记忆力耗尽了。所以我决定通过cron每x分钟杀死一次python,因为我没有找到解决方案。

我用这个:

pkill -f scriptName.py

于 2020-04-13T19:52:31.473 回答