【发布时间】:2019-01-27 01:38:39
【问题描述】:
我正在尝试将一个大型 json 对象加载到内存中,然后对数据执行一些操作。但是,我注意到读取 json 文件后 RAM 大幅增加 -即使对象超出范围。
这里是代码
import json
import objgraph
import gc
from memory_profiler import profile
@profile
def open_stuff():
with open("bigjson.json", 'r') as jsonfile:
d= jsonfile.read()
jsonobj = json.loads(d)
objgraph.show_most_common_types()
del jsonobj
del d
print ('d')
gc.collect()
open_stuff()
我尝试在 Windows 中使用 Python 版本 2.7.12 和 Debian 9 中使用 Python 版本 2.7.13 运行此脚本,但我发现 Linux 中的 Python 存在问题。
在 Windows 中,当我运行脚本时,它会在读取 json 对象并在范围内时占用大量 RAM(如预期的那样),但在操作完成后会释放它(如预期的那样)。
list 3039184
dict 413840
function 2200
wrapper_descriptor 1199
builtin_function_or_method 819
method_descriptor 651
tuple 617
weakref 554
getset_descriptor 362
member_descriptor 250
d
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
5 16.9 MiB 16.9 MiB @profile
6 def open_stuff():
7 16.9 MiB 0.0 MiB with open("bigjson.json", 'r') as jsonfile:
8 197.9 MiB 181.0 MiB d= jsonfile.read()
9 1393.4 MiB 1195.5 MiB jsonobj = json.loads(d)
10 1397.0 MiB 3.6 MiB objgraph.show_most_common_types()
11 402.8 MiB -994.2 MiB del jsonobj
12 221.8 MiB -181.0 MiB del d
13 221.8 MiB 0.0 MiB print ('d')
14 23.3 MiB -198.5 MiB gc.collect()
但是在 LINUX 环境中,即使对 JSON 对象的所有引用都已删除,仍会使用超过 500MB 的 RAM。
list 3039186
dict 413836
function 2336
wrapper_descriptor 1193
builtin_function_or_method 765
method_descriptor 651
tuple 514
weakref 480
property 273
member_descriptor 250
d
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
5 14.2 MiB 14.2 MiB @profile
6 def open_stuff():
7 14.2 MiB 0.0 MiB with open("bigjson.json", 'r') as jsonfile:
8 195.1 MiB 181.0 MiB d= jsonfile.read()
9 1466.4 MiB 1271.3 MiB jsonobj = json.loads(d)
10 1466.8 MiB 0.4 MiB objgraph.show_most_common_types()
11 694.8 MiB -772.1 MiB del jsonobj
12 513.8 MiB -181.0 MiB del d
13 513.8 MiB 0.0 MiB print ('d')
14 513.0 MiB -0.8 MiB gc.collect()
在 Debian 9 和 Python 3.5.3 中运行的相同脚本使用较少的 RAM,但会泄漏相应数量的 RAM。
list 3039266
dict 414638
function 3374
tuple 1254
wrapper_descriptor 1076
weakref 944
builtin_function_or_method 780
method_descriptor 780
getset_descriptor 477
type 431
d
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
5 17.2 MiB 17.2 MiB @profile
6 def open_stuff():
7 17.2 MiB 0.0 MiB with open("bigjson.json", 'r') as jsonfile:
8 198.3 MiB 181.1 MiB d= jsonfile.read()
9 1057.7 MiB 859.4 MiB jsonobj = json.loads(d)
10 1058.1 MiB 0.4 MiB objgraph.show_most_common_types()
11 537.5 MiB -520.6 MiB del jsonobj
12 356.5 MiB -181.0 MiB del d
13 356.5 MiB 0.0 MiB print ('d')
14 355.8 MiB -0.8 MiB gc.collect()
是什么导致了这个问题? 两个版本的 Python 都运行 64 位版本。
编辑 - 连续多次调用该函数会导致更奇怪的数据,json.loads 函数每次调用时使用较少的 RAM,在第三次尝试后 RAM 使用稳定,但较早泄漏的 RAM 确实如此不会被释放..
list 3039189
dict 413840
function 2339
wrapper_descriptor 1193
builtin_function_or_method 765
method_descriptor 651
tuple 517
weakref 480
property 273
member_descriptor 250
d
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
5 14.5 MiB 14.5 MiB @profile
6 def open_stuff():
7 14.5 MiB 0.0 MiB with open("bigjson.json", 'r') as jsonfile:
8 195.4 MiB 180.9 MiB d= jsonfile.read()
9 1466.5 MiB 1271.1 MiB jsonobj = json.loads(d)
10 1466.9 MiB 0.4 MiB objgraph.show_most_common_types()
11 694.8 MiB -772.1 MiB del jsonobj
12 513.9 MiB -181.0 MiB del d
13 513.9 MiB 0.0 MiB print ('d')
14 513.1 MiB -0.8 MiB gc.collect()
list 3039189
dict 413842
function 2339
wrapper_descriptor 1202
builtin_function_or_method 765
method_descriptor 651
tuple 517
weakref 482
property 273
member_descriptor 253
d
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
5 513.1 MiB 513.1 MiB @profile
6 def open_stuff():
7 513.1 MiB 0.0 MiB with open("bigjson.json", 'r') as jsonfile:
8 513.1 MiB 0.0 MiB d= jsonfile.read()
9 1466.8 MiB 953.7 MiB jsonobj = json.loads(d)
10 1493.3 MiB 26.6 MiB objgraph.show_most_common_types()
11 723.9 MiB -769.4 MiB del jsonobj
12 723.9 MiB 0.0 MiB del d
13 723.9 MiB 0.0 MiB print ('d')
14 722.4 MiB -1.5 MiB gc.collect()
list 3039189
dict 413842
function 2339
wrapper_descriptor 1202
builtin_function_or_method 765
method_descriptor 651
tuple 517
weakref 482
property 273
member_descriptor 253
d
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
5 722.4 MiB 722.4 MiB @profile
6 def open_stuff():
7 722.4 MiB 0.0 MiB with open("bigjson.json", 'r') as jsonfile:
8 722.4 MiB 0.0 MiB d= jsonfile.read()
9 1493.1 MiB 770.8 MiB jsonobj = json.loads(d)
10 1493.4 MiB 0.3 MiB objgraph.show_most_common_types()
11 724.4 MiB -769.0 MiB del jsonobj
12 724.4 MiB 0.0 MiB del d
13 724.4 MiB 0.0 MiB print ('d')
14 722.9 MiB -1.5 MiB gc.collect()
Filename: testjson.py
Line # Mem usage Increment Line Contents
================================================
17 14.2 MiB 14.2 MiB @profile
18 def wow():
19 513.1 MiB 498.9 MiB open_stuff()
20 722.4 MiB 209.3 MiB open_stuff()
21 722.9 MiB 0.6 MiB open_stuff()
编辑 2:有人建议这是 Why does my program's memory not release? 的副本,但所讨论的内存量与另一个问题中讨论的“小页面”相去甚远。
【问题讨论】:
-
如果您反复调用
open_stuff,内存使用是否会持续增长? -
@melpomene 是的,在最初的 513MB 被第一次调用占用后,看起来每次调用增加了大约 200MB
-
实际上看起来第一次调用将它增加到 513MB,第二次增加到 722MB,第三次保持在 723MB。每次调用 json.loads 调用都会使用更少的 RAM。
-
@Miguel 上下文管理器的要点是您不必在完成后手动关闭资源。 docs.python.org/2.5/whatsnew/pep-343.html 不过,我也试过了,但没有区别。
标签: python linux memory-leaks