迭代数据库结果时,如何在 App Engine (python) 中收集内存垃圾
How is memory garbage collected in app engine (python) when iterating over db results
我有一些代码遍历数据库实体,并在任务中运行 - 见下文。
在 App Engine 上,我收到 Exceeded soft private memory limit
错误,实际上检查 memory_usage().current()
确认了问题。有关日志语句的输出,请参见下文。似乎每次获取一批 foo 时,内存都会增加。
我的问题是:为什么内存没有被垃圾回收?我希望在循环的每次迭代中(分别是 while
循环和 for
循环)名称 foos
和 [=19= 的重复使用] 会导致 foos
和 foo
用来指向的对象将是 'de-referenced' (即变得不可访问),因此有资格进行垃圾收集,然后在内存紧张时被垃圾收集.但显然它没有发生。
from google.appengine.api.runtime import memory_usage
batch_size = 10
dict_of_results = {}
results = 0
cursor = None
while True:
foos = models.Foo.all().filter('status =', 6)
if cursor:
foos.with_cursor(cursor)
for foo in foos.run(batch_size = batch_size):
logging.debug('on result #{} used memory of {}'.format(results, memory_usage().current()))
results +=1
bar = some_module.get_bar(foo)
if bar:
try:
dict_of_results[bar.baz] += 1
except KeyError:
dict_of_results[bar.baz] = 1
if results >= batch_size:
cursor = foos.cursor()
break
else:
break
并在 some_module.py
def get_bar(foo):
for bar in foo.bars:
if bar.status == 10:
return bar
return None
logging.debug 的输出(缩短)
on result #1 used memory of 43
on result #2 used memory of 43
.....
on result #20 used memory of 43
on result #21 used memory of 49
.....
on result #32 used memory of 49
on result #33 used memory of 54
.....
on result #44 used memory of 54
on result #45 used memory of 59
.....
on result #55 used memory of 59
.....
.....
.....
on result #597 used memory of 284.3
Exceeded soft private memory limit of 256 MB with 313 MB after servicing 1 requests total
您可能看错了方向。
查看此问答以了解检查垃圾回收的方法和可能的替代解释:
看起来您的批处理解决方案与数据库的批处理冲突,导致大量额外的批处理悬而未决。
当你运行query.run(batch_size=batch_size)
时,db会运行查询直到完成整个limit。当您到达批次末尾时,db 将抓取下一批。但是,在 db 执行此操作后,您立即退出循环并重新开始。这意味着批次 1 -> n 都将在内存中存在两次。一次用于最后一个查询提取,一次用于您的下一个查询提取。
如果您想遍历所有实体,只需让 db 处理批处理即可:
foos = models.Foo.all().filter('status =', 6)
for foo in foos.run(batch_size = batch_size):
results +=1
bar = some_module.get_bar(foo)
if bar:
try:
dict_of_results[bar.baz] += 1
except KeyError:
dict_of_results[bar.baz] = 1
或者,如果您想自己处理批处理,请确保数据库不进行任何批处理:
while True:
foo_query = models.Foo.all().filter('status =', 6)
if cursor:
foo_query.with_cursor(cursor)
foos = foo_query.fetch(limit=batch_size)
if not foos:
break
cursor = foos.cursor()
我有一些代码遍历数据库实体,并在任务中运行 - 见下文。
在 App Engine 上,我收到 Exceeded soft private memory limit
错误,实际上检查 memory_usage().current()
确认了问题。有关日志语句的输出,请参见下文。似乎每次获取一批 foo 时,内存都会增加。
我的问题是:为什么内存没有被垃圾回收?我希望在循环的每次迭代中(分别是 while
循环和 for
循环)名称 foos
和 [=19= 的重复使用] 会导致 foos
和 foo
用来指向的对象将是 'de-referenced' (即变得不可访问),因此有资格进行垃圾收集,然后在内存紧张时被垃圾收集.但显然它没有发生。
from google.appengine.api.runtime import memory_usage
batch_size = 10
dict_of_results = {}
results = 0
cursor = None
while True:
foos = models.Foo.all().filter('status =', 6)
if cursor:
foos.with_cursor(cursor)
for foo in foos.run(batch_size = batch_size):
logging.debug('on result #{} used memory of {}'.format(results, memory_usage().current()))
results +=1
bar = some_module.get_bar(foo)
if bar:
try:
dict_of_results[bar.baz] += 1
except KeyError:
dict_of_results[bar.baz] = 1
if results >= batch_size:
cursor = foos.cursor()
break
else:
break
并在 some_module.py
def get_bar(foo):
for bar in foo.bars:
if bar.status == 10:
return bar
return None
logging.debug 的输出(缩短)
on result #1 used memory of 43
on result #2 used memory of 43
.....
on result #20 used memory of 43
on result #21 used memory of 49
.....
on result #32 used memory of 49
on result #33 used memory of 54
.....
on result #44 used memory of 54
on result #45 used memory of 59
.....
on result #55 used memory of 59
.....
.....
.....
on result #597 used memory of 284.3
Exceeded soft private memory limit of 256 MB with 313 MB after servicing 1 requests total
您可能看错了方向。
查看此问答以了解检查垃圾回收的方法和可能的替代解释:
看起来您的批处理解决方案与数据库的批处理冲突,导致大量额外的批处理悬而未决。
当你运行query.run(batch_size=batch_size)
时,db会运行查询直到完成整个limit。当您到达批次末尾时,db 将抓取下一批。但是,在 db 执行此操作后,您立即退出循环并重新开始。这意味着批次 1 -> n 都将在内存中存在两次。一次用于最后一个查询提取,一次用于您的下一个查询提取。
如果您想遍历所有实体,只需让 db 处理批处理即可:
foos = models.Foo.all().filter('status =', 6)
for foo in foos.run(batch_size = batch_size):
results +=1
bar = some_module.get_bar(foo)
if bar:
try:
dict_of_results[bar.baz] += 1
except KeyError:
dict_of_results[bar.baz] = 1
或者,如果您想自己处理批处理,请确保数据库不进行任何批处理:
while True:
foo_query = models.Foo.all().filter('status =', 6)
if cursor:
foo_query.with_cursor(cursor)
foos = foo_query.fetch(limit=batch_size)
if not foos:
break
cursor = foos.cursor()