【问题标题】:Heap grows for every image loading每次加载图像时堆都会增长
【发布时间】:2015-12-16 12:51:35
【问题描述】:

在我的项目中,我有最简单的列表视图和适配器,可以使用 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,...),似乎只剩下位图。

所以我的问题:

为什么 Android 不能分配 140 甚至 500 KB,即使当前有大约 3000 KB 的空闲堆?

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

【问题讨论】:

    标签: java android performance memory garbage-collection


    【解决方案1】:

    每个解码后的图像大约 140-150 kbytes

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

    为什么 Android 不能分配 140 甚至 500 KB,即使当前有大约 3000 KB 的空闲堆?

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

    引用myself,为便于阅读进行了少量编辑:

    在 Java 中,在 JVM 上,如果没有足够大的单个内存块,这意味着我们 实际上内存不足(关于这个请求,无论如何)。

    然而,JVM 有一个 Dalvik VM 没有的东西:压缩垃圾收集器。

    当我们使用运行在支持垃圾收集的运行时上的语言编写程序时, 我们创建了一堆对象,然后我们发布了这些对象的一些子集。另一个 我们坚持了一段时间,因为我们仍在使用它们。例如,在 Android 中, 我们的Application,我们的ContentProviders,我们的Threads,我们自己的静态数据成员, 等等将存在相当长的一段时间,在许多情况下是在整个过程中。 他们可以到达的任何东西也不会被垃圾收集。所以,我们分配了一堆 内存并在我们的应用运行时释放部分内存。

    使用压缩垃圾收集器,长寿命的对象会在内存中滑动, 允许合并释放的内存块。网是所有空闲堆 空间应该作为一个连续的块可用,可以分配。

    (细节比这更复杂,但这是堆栈溢出的答案,而不是论文)

    使用非压缩垃圾收集器,不会在内存中滑动任何内容。我们结束 带有堆碎片,就像曾经的原始 16MB(或 32MB、48MB 或其他)一样 堆现在是一堆散布着空闲内存的对象。即使我们可能 有 10MB 的可用堆空间,如果该可用堆空间的最大单个块仅 1MB,我们不能分配一个 8MB Bitmap,因为没有足够大的空闲块。

    Dalvik VM 有一个非压缩垃圾收集器。

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

    【讨论】:

    • 谢谢。乍一看是有道理的。堆增长的大小是多少,从日志中我看到堆的下一个大小:38228K、38376K、38524K、38672K。每次增长大约只有100-200K。但在我的设备中 dalvik.vm.heapminfree = 500K 和 dalvik.vm.heapmaxfree = 2m。所以堆平均应该增长1M..我认为下一张图像有空闲的单个块就足够了。可能我完全错了..
    • @KirillOlenyov:“所以堆平均应该增长 1M”——我不知道你是怎么得出这个结论的。当GC_FOR_ALLOC 条件到达时,我不知道 GC 引擎使用的算法来分配多少系统 RAM 以用于扩展堆。
    • 好吧,没关系)你对如何调查内存碎片有什么建议吗?
    • @KirillOlenyov:“你对如何调查内存碎片有什么建议吗?” - 并不真地。我不知道有什么方法可以确定堆中可用的块大小。相反,我会专注于使用更少的内存并重用您分配的内存。我给了a presentation on "Top Ten Memory Management and Tuning Tips" at Samsung's 2014 developer conference一些建议。
    猜你喜欢
    • 2023-03-18
    • 2013-08-09
    • 1970-01-01
    • 2012-01-15
    • 2023-04-11
    • 2019-12-04
    • 2023-03-29
    • 2019-05-12
    • 2016-12-24
    相关资源
    最近更新 更多