我有一个应用程序可以在线下载 csv 文件,然后将其保存在本地,因此即使它处于脱机状态,该应用程序也能正常工作。我的问题是当用户关闭应用程序然后立即再次打开它时,应用程序在解析保存的 csv 文件时挂起并抛出 OutOfMemoryError。但是,我注意到当我在几分钟后再次打开该应用程序时,它工作得很好。下载、解析和保存在不同的线程上完成。有什么办法可以解决这个问题?
1 回答
一种可能性:内存不足错误可能更多地与过度工作的 GC 相关,而不是与实际内存不足有关。如果您分配大块内存,然后释放它们,然后分配更大的块,您会达到这样一个点,即您有大量可用内存占用空间,但由于它们不够大而无法使用。GC 疯狂地试图移动东西并将这些碎片合并到一个连续的块中以进行下一次分配,但它不会因为花费太长时间而看起来很糟糕,它只会抛出 OutOfMemory 异常,即使理论上 90% 的内存是可用的(如果你能给它一分钟,它将可用)。
在你的情况下,我怀疑 ArrayList。它保留了一个引用数组。当您添加条目时,它会添加到数组中。当它跑到最后时,它会分配一个新的、更大的,并释放旧的。如果您保持忙碌,这些丢弃物就会堆积起来。哈希表也有类似的问题。LinkedList 和 TreeMap 不会,因为它们使用少量内存。
我对 Android 了解不多,但我猜当你短暂关闭它时应用程序并没有真正关闭,所以当你重新启动它时,它与以前一样是自由内存碎片执行。如果你等待一段时间,它可能是一个新的执行。即使不是,GC 也有时间清理一切,你很好。
您想要的解决方案可能是每次“启动”系统时强制进行垃圾收集(System.gc())。它使 GC 有机会在为您分配空间之前将所有内容整理好,并且不会花费很长时间。从某种意义上说,您授予 GC 将您的程序锁定半秒钟的权限,而它自己不会这样做。(如果确实如此,它会选择一个尴尬的时间来做这件事——比如说,在用户输入文本的时候。)
通过使用链接集合来避免大型数组是另一种解决方案,但数组速度很快,当您可以腾出半秒的用户时间时,没有理由切换。
希望这可以帮助。如果这一次不是问题,也许下次会是。
另外: 不幸的是, System.gc() 只是一个“建议”。它可能没有做我们希望它做的工作。或者您可能会在通话后遇到麻烦。我之前应该提到的另一个重大修复是将ArrayList 的初始大小设置为非常大,如果这是您正在使用的。将其设置为所需大小的两到三倍可能会在一次运行中为您节省十倍的内存量 - 并节省时间。这适用于任何基于数组的结构(哈希表和普通数组)。除此之外,如果你能绕过它们的缺点,像 LinkedList 这样的基于指针的结构就不会有这个问题。