游标窗口:窗口已满

42

我已经创建了一个由查询结果填充的ListView。
它可以正常工作,但是在LogCat中我收到了这个消息:

Cursor Window: Window is full: requested allocation 444 bytes, free space 363 bytes, window size 2097152 bytes

加载/显示ListView需要几分钟时间。

我的查询返回大约3700行字符串/整数/双精度浮点数,每行有30列;没有图像或特定的数据类型。

这条消息到底是什么意思,我该如何避免它?
是否可以通过更改此Cursor Window来提高性能?


2
请参考此链接:https://dev59.com/WGct5IYBdhLWcg3wuf1b - Chintan Soni
谢谢,但我已经看过这个问题了,我不能使用SD卡。 - Tirrel
是的,我换工作了 :)抱歉,没有更新。 - Tirrel
@RichardLeMesurier 你是怎么解决的?我也遇到了同样的问题。 - John Joe
@Tirrel,你是怎么解决的?我也遇到了同样的问题。 - John Joe
显示剩余2条评论
4个回答

23

根据我的经验,这意味着查询结果对于游标窗口来说太大了,需要请求更多的内存。大多数情况下,这个请求会被满足,但在低端设备上可能会抛出异常。

我不知道涉及的应用程序的具体情况,但是您提到了一个 ListView。ListView 无法一次显示 3700 行,无限列表可以帮助按需加载数据。

我的建议是将查询分解成多个返回较小结果的查询,并在运行下一个查询之前关闭它们。在每个连续的查询后,合并结果。


一个ListView可以显示超过10,000行的光标(基于我的经验)。这个问题绝对是关于内存,而不是行数。 - matreshkin
@matreshkin同意,这就是ListView的全部意义:根据屏幕上可见的内容来惰性地加载和渲染行。 - Jeffrey Blattman
1
顺便提一句,现在我看到@Delblanco写道“一个ListView无法一次显示3700行”。听起来有点混淆,但实际上,在一个窗口中附加3700个视图似乎是非常特殊的情况 :) - matreshkin
1
这个答案存在误导:为了确定此日志信息是否指示了一个真正的问题,你应该比较当前的“请求分配”值和总“窗口大小”值。更多细节,请查看下面@user1575326的答案。 - yvolk
“ListView 无法一次显示3700行”- ListView 不会一次性将所有视图加载到内存中。这就是 ListViewScrollView 中的 LinearLayout 之间的区别。 - Jeffrey Blattman

15

简短版:

经过一些调查,发现该消息是正常操作的一部分,不必担心。它被记录在“警告”级别,但我认为这只是过于积极了。

详细版:

这是一个明确标记为“窗口”游标,这意味着随着获得新记录,旧记录将被丢弃。在最简单的形式中,这样的“窗口”实现可能包含最多N行总计,可能带有一些预读取。 但在此实现中,窗口大小由总大小定义。内存中保存的行数基于可以适应整个窗口的数量,并且在运行时会发生变化(这可能更像是“缓冲”游标而不是“窗口”游标)。

作为具有(软-?)限制大小的缓冲实现,仅当缓冲区太满以容纳下一行时,才会丢弃最早的行。在这种情况下,会删除1个或更多旧的行。这个“保持分配行直到我们不能再容纳更多,然后释放我们缓冲区中最老的记录并重试”过程似乎是完全正常和预期的,这是保持内存空间受限的正常过程。

我基于在此处阅读源代码以及一些推理得出了这个结论: https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/CursorWindow.cpp

为什么人们会谈论图像和其他大型LOB?

如果单行大小大于整个“窗口”(缓冲区)的大小,则此策略将失效,您将面临实际问题。

这是 @op 收到的消息:

Cursor Window: Window is full: requested allocation 444 bytes, free space 363 bytes, window size 2097152 bytes

这是 @vovahost 收到的消息:

CursorWindow: Window is full: requested allocation 2202504 bytes, free space 2076560 bytes, window size 2097152 bytes

第一种情况,请求的分配比窗口大小小得多。我预计类似的消息会重复发出,具有相同的窗口大小和不同的请求分配大小。每次打印这个时,从较大的窗口中释放内存,并进行新的分配。这是正常和健康的操作。

第二种情况,请求的分配大小超过了整个窗口大小。这是一个实际的问题,需要以更可流式化的方式存储和读取数据。

区别在于“长度”(总行数)与“宽度”(最大单行的内存成本)。前者(@tirrel的问题)不是问题,但后者(@vovahost的问题)是。


很好的且详尽的回答。 - Daniel F

8

我也遇到了这个问题。在我的情况下,我将一张2.2MB的图片保存到了数据库中。当使用Cursor.getBlob()从数据库加载数据时,会在日志中看到以下信息:

CursorWindow: Window is full: requested allocation 2202504 bytes, free space 2076560 bytes, window size 2097152 bytes

如果我尝试检索连续行的任何数据(String、number等),我会得到这个消息,返回为null,而没有任何错误信息。 解决方案是删除2.2 MB的blob。我不知道在Android中是否可能从数据库加载更大的blob。


1
此外,请注意更改窗口会有IPC开销。 因此,如果光标具有大量项目并与列表视图一起使用,则快速导航会导致窗口更改,从而频繁进行IPC。如果系统负载过重,这可能会导致ANR。

您的回答可以通过添加更多支持信息来改善。请[编辑]以添加进一步的细节,例如引用或文档,以便其他人可以确认您的答案是否正确。您可以在帮助中心中找到有关如何撰写好的答案的更多信息。 - Community

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