在我的项目中,我有最简单的listview和适配器,可以使用Picasso加载图像。每个解码的图像大约为140-150 KB。
但是在最初的30-60秒(毕加索存储器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,...),似乎只剩下位图了。
所以我的问题是:
为什么即使当前活动堆中大约有3000 KB可用空间,Android也无法分配140甚至500 KB?
可以是特定设备或Android版本的发行吗?
最佳答案
每个解码图像约为140-150 KB
对于ListView
行来说,这是相当大的,因为这相当于大约390x390的正方形图像。
为什么即使当前活动堆中大约有3000 KB可用空间,Android也无法分配140甚至500 KB?
因为“在当前活动堆中没有3000 KB的空间”在Android中基本上是没有意义的概念。您正在通过GC_FOR_ALLOC
,因为没有单个可用内存块足以满足您的请求。
引用myself,并进行轻度编辑以提高可读性:
在Java中,在JVM上,如果没有足够大的单个内存块,则意味着我们
实际上是内存不足(无论如何,对于此请求)。
但是,JVM具有Dalvik VM所没有的一件事:压缩垃圾收集器。
当我们使用在支持垃圾回收的运行时上运行的语言编写程序时,
我们创建了一堆对象,然后发布这些对象的一些子集。另一个
我们会保留一段时间,因为我们仍在使用它们。例如,在Android中,
我们的Application
,我们的ContentProvider
,我们的Thread
,我们自己的静态数据成员,
等等会持续很长一段时间,在许多情况下会持续整个过程。
他们可以到达的任何东西也不会被垃圾收集。因此,我们分配了一堆
内存,并在我们的应用运行时释放部分内存。
使用压缩垃圾收集器,长寿命的对象会在内存中滑动,
以便合并释放的内存块。网络是所有空闲堆
该空间应作为一个连续的块可用,可以分配。
(细节要复杂得多,但这是堆栈溢出的答案,而不是论文)
使用非紧凑型垃圾回收器,内存中不会滑动任何东西。我们结束
带有堆碎片,就像以前原始的16MB(或32MB或48MB,或其他)一样
现在,堆是一堆散布着空闲内存的对象。即使我们可能
拥有10MB的可用堆空间,如果该可用堆空间的最大单个块仅为
1MB,我们无法分配8MB Bitmap
,因为没有足够大的可用块。
Dalvik VM具有非紧凑型垃圾收集器。
请注意,ART(Android 5.0+)具有压缩的垃圾收集器,但仅当应用程序在后台时才压缩。