凌乱的内存错误,奇怪的分配尝试

有时,随机启动时,Volley会崩溃我的应用程序,它将在应用程序类中崩溃,并且用户将无法再次打开应用程序,直到进入设置并清除应用程序数据

java.lang.OutOfMemoryError at com.android.volley.toolbox.DiskBasedCache.streamToBytes(DiskBasedCache.java:316) at com.android.volley.toolbox.DiskBasedCache.readString(DiskBasedCache.java:526) at com.android.volley.toolbox.DiskBasedCache.readStringStringMap(DiskBasedCache.java:549) at com.android.volley.toolbox.DiskBasedCache$CacheHeader.readHeader(DiskBasedCache.java:392) at com.android.volley.toolbox.DiskBasedCache.initialize(DiskBasedCache.java:155) at com.android.volley.CacheDispatcher.run(CacheDispatcher.java:84) 

“diskbasedbache”试图分配超过1千兆字节的内存,原因不明

我将如何使这不会发生? 这似乎是Volley的一个问题,或者是一个基于定制磁盘的caching的问题,但是我不能立即看到(从堆栈跟踪)如何“清除”这个caching,或者做一个条件检查或处理这个exception

Insight深表谢意

Solutions Collecting From Web of "凌乱的内存错误,奇怪的分配尝试"

streamToBytes() ,首先它会通过caching文件的长度来获取新的字节,你的caching文件是否比应用程序的最大堆大小要大?

 private static byte[] streamToBytes(InputStream in, int length) throws IOException { byte[] bytes = new byte[length]; ... } public synchronized Entry get(String key) { CacheHeader entry = mEntries.get(key); File file = getFileForKey(key); byte[] data = streamToBytes(..., file.length()); } 

如果要清除caching,则可以在清除时间后保留DiskBasedCache引用,使用ClearCacheRequest并将该caching实例传递给:

 File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); DiskBasedCache cache = new DiskBasedCache(cacheDir); RequestQueue queue = new RequestQueue(cache, network); queue.start(); // clear all volley caches. queue.add(new ClearCacheRequest(cache, null)); 

这样会清除所有的caching,所以我build议你仔细使用它。 当然,你可以做conditional check ,只是迭代cacheDir文件,估计是太大,然后将其删除。

 for (File cacheFile : cacheDir.listFiles()) { if (cacheFile.isFile() && cacheFile.length() > 10000000) cacheFile.delete(); } 

Volley不是作为大数据caching解决schemedevise的,它是常见的请求caching,不会随时存储大量数据。

————-更新于2014-07-17 ————-

实际上,清除所有的caching是最终的办法,也不是明智的办法,当我们肯定会的时候,我们应该压制这些大的请求使用caching,如果不确定的话? 我们仍然可以确定响应数据大小是否大,然后调用setShouldCache(false)来禁用它。

 public class TheRequest extends Request { @Override protected Response<String> parseNetworkResponse(NetworkResponse response) { // if response data was too large, disable caching is still time. if (response.data.length > 10000) setShouldCache(false); ... } } 

我遇到了同样的问题。

我们知道我们在caching初始化时没有大小为GB的文件。 读取标题string时也会发生这种情况,string永远不应该是GB。

所以看起来像readLong读取的长度是不正确的。

我们有两个应用程序的设置大致相同,只是一个应用程序在启动时创build了两个独立的进程。 主要的应用程序进程和一个SyncAdapter进程跟在同步适配器模式之后。 只有具有两个进程的应用程序才会崩溃。 这两个进程将独立初始化caching。

但是,DiskBasedCache为这两个进程使用相同的物理位置。 我们最终得出的结论是并发初始化导致同时读取和写入相同的文件,导致大小参数的读取不正确。

我没有充分的证据表明这是问题,但我打算在testing应用程序上进行validation。

在短期内,我们刚刚捕获了streamToBytes中过大的字节分配,并抛出IOException,以便Volley捕获exception并删除文件。 但是,为每个进程使用单独的磁盘caching可能会更好。

  private static byte[] streamToBytes(InputStream in, int length) throws IOException { byte[] bytes; // this try-catch is a change added by us to handle a possible multi-process issue when reading cache files try { bytes = new byte[length]; } catch (OutOfMemoryError e) { throw new IOException("Couldn't allocate " + length + " bytes to stream. May have parsed the stream length incorrectly"); } int count; int pos = 0; while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) { pos += count; } if (pos != length) { throw new IOException("Expected " + length + " bytes, read " + pos + " bytes"); } return bytes; } 

一旦发生问题,似乎在每次后续初始化时都会重复出现,指向无效的caching标头。

幸运的是,这个问题已经在Volley官方回购中得到修复:

请参阅android-volley镜像中的相关问题: