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

标签 java android performance memory garbage-collection

在我的项目中,我有最简单的 ListView 和适配器,使用 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,当前 Activity 堆大小大约有 3 MB 可用空间。

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

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

所以我的问题:

为什么即使当前 Activity 堆有大约 3000 kbytes 可用,Android 也无法分配 140 甚至 500 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?

因为“当前 Activity 堆中的 3000 KB 可用空间”在 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+) 有一个可以压缩的垃圾收集器,但仅当应用位于后台时才有效。

关于java - 每次加载图像时堆都会增长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32667821/

相关文章:

android - 从 URL 在 VideoView Android 中查找时出错

android - CardView翻转动画

performance - F# 函数可以被视为尾递归吗?它使用 TailCall .net 操作码

java - 每个进程创建 log4j 日志文件

java - 如何使用 thymeleaf 做到这一点?

java - 自动启动和停止方法

javascript - Object.defineProperty 的调用后/运行时性能影响

java - 使用 Spring Jpa 为服务插入语句

java - 默认 switch case 之后的 if 语句 - Java

c++ - 为什么编译器对原始/标准数组而不是 vector 使用 XMM 寄存器?