每次图像加载时堆都会增长

Heap grows for every image loading

在我的项目中,我有最简单的列表视图和适配器,使用 Picasso 加载图像。每个解码图像大约为 140-150 kbytes.. 但是在前 30-60 秒(当 Picasso 内存 LruCache 填充时)滚动是滞后的。

这是logcat的一部分:

09-19 14:11:01.435    4676-5700/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed 270K, 11% free 34374K/38228K, paused 60ms, total 61ms
09-19 14:11:01.435    4676-5700/com.test I/dalvikvm-heap﹕ Grow heap (frag case) to 34.487MB for 148408-byte allocation
09-19 14:11:01.505    4676-4676/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed 9K, 11% free 34510K/38376K, paused 66ms, total 66ms
09-19 14:11:06.875    4676-5701/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed 132K, 11% free 34497K/38376K, paused 39ms, total 39ms
09-19 14:11:06.875    4676-5701/com.test I/dalvikvm-heap﹕ Grow heap (frag case) to 34.606MB for 147760-byte allocation
09-19 14:11:06.915    4676-4676/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed <1K, 11% free 34641K/38524K, paused 41ms, total 41ms
09-19 14:11:11.375    4676-5702/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed 110K, 11% free 34650K/38524K, paused 58ms, total 59ms
09-19 14:11:11.385    4676-5702/com.test I/dalvikvm-heap﹕ Grow heap (frag case) to 34.757MB for 149704-byte allocation
09-19 14:11:11.445    4676-4676/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed <1K, 11% free 34796K/38672K, paused 64ms, total 64ms
09-19 14:11:14.615    4676-5699/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed 119K, 11% free 34796K/38672K, paused 53ms, total 53ms
09-19 14:11:14.625    4676-5699/com.test I/dalvikvm-heap﹕ Grow heap (frag case) to 34.900MB for 149704-byte allocation
09-19 14:11:14.655    4676-4676/com.test D/dalvikvm﹕ GC_FOR_ALLOC freed <1K, 10% free 34942K/38820K, paused 37ms, total 37ms

我在这里看到的:

堆会随着图像下载和解码而增长。

在堆增长之前,Dalvik 尝试 GC 一些内存。

根据每个 GL_FOR_ALLOC。

当前的活动堆大小大约有 3 MB 空闲

对于列表视图中的每个下一张图像,我们需要大约 300-500kbytes 的内存 = 下载字节(大约 60-100kb)+ 位图(140kb)+ 网络人员 + 视图人员。 我在 Android Studio 中使用内存分配跟踪进行了确认。

所以在堆增长之前,一些内存和没有引用的对象被收集(释放 270K,释放 119K,...),似乎只剩下 Bitmap。

所以我的问题:

为什么 Android 无法分配 140 甚至 500 kbytes,即使当前活动堆有大约 3000 kbytes 空闲?

可能是特定设备或 Android 版本的问题?

Every decoded image is about 140-150 kbytes

这对于 ListView 行来说相当大,因为这相当于大约 390x390 的正方形图像。

Why Android cannot allocate 140 or even 500 kbytes even if there is about 3000 kbytes free of current live heap?

因为“3000 kbytes free of current live heap”在Android中基本上是一个毫无意义的概念。您正在经历 GC_FOR_ALLOC 因为没有足够大的可用内存块来满足您的请求。

引用 myself,为提高可读性进行了少量编辑:

In Java, on the JVM, if there is no single block of memory big enough, that means that we are actually out of memory (with respect to this request, anyway).

However, the JVM has one thing that the Dalvik VM does not: a compacting garbage collector.

When we write programs in a language running on a runtime supporting garbage collection, we create a bunch of objects, and we later release some subset of those objects. The other ones we hold onto for a while, because we are still using them. In Android, for example, our Application, our ContentProviders, our Threads, our own static data members, and so on are going to live for quite some time, in many cases for the duration of the process. Anything they can reach also will not get garbage-collected. So, we allocate a bunch of memory and release some of that memory as our app runs.

With a compacting garbage collector, the long-lived objects get slid around in memory, to allow the freed-up blocks of memory to be coalesced. The net is that all free heap space should be available as one contiguous block, eligible for allocation.

(the details are more complicated than that, but this is a Stack Overflow answer, not a dissertation)

With a non-compacting garbage collector, nothing is slid around in memory. We wind up with heap fragmentation, as what was once a pristine 16MB (or 32MB, or 48MB, or whatever) heap now is a bunch of objects with interspersed free memory. Even though we might have 10MB of free heap space, if the largest single block of that free heap space is only 1MB, we cannot allocate an 8MB Bitmap, because there is no free block big enough.

The Dalvik VM has a non-compacting garbage collector.

请注意,ART (Android 5.0+) 有一个压缩垃圾收集器,但仅当应用程序在后台时才有效。