【问题标题】:Weird behavior of Garbage Collector Android垃圾收集器Android的奇怪行为
【发布时间】:2014-12-21 08:22:52
【问题描述】:

对于应用程序,我正在努力提高效率非常重要。为了避免不必要的垃圾收集,我分配了很多内存(为了增加堆大小)并立即释放它。但是,无论我有多少可用内存,垃圾收集仍然会发生。

当我不进行这种额外的内存分配/释放时,我只有 24% 的可用内存,而下面的日志中为 63%,但 GC 调用的数量是相同的。 GC 暂停是相同的。执行时间(程序)是相同的。

谁能解释一下这种奇怪的 GC 行为?为什么GC_FOR_ALLOC 会被触发?有大量可用内存。

在我的程序中主要的操作是:文件读取、加密、数据传输。 对于数据传输和文件读取,我使用ByteBufferFileChannelSocketChannel。 大量的 ByteBuffer 分配会成为瓶颈吗? 好像我也没有任何内存泄漏。

在 GalaxyS3 (Android 4.4.2) 和 GalaxyS2 (Android 4.1.2) 上测试的程序

12-21 00:30:54.973: I/dalvikvm-heap(30222): Grow heap (frag case) to 44.361MB for 25000016-byte     allocation
12-21 00:30:55.463: D/dalvikvm(30222): GC_FOR_ALLOC freed 43K, 8% free 42893K/46416K, paused 20ms, total 20ms
12-21 00:31:01.900: D/dalvikvm(30222): GC_FOR_ALLOC freed 28367K, 63% free 18615K/49480K, paused 39ms, total 39ms
12-21 00:31:07.606: D/dalvikvm(30222): GC_FOR_ALLOC freed 3600K, 63% free 18648K/49480K, paused 42ms, total 42ms
12-21 00:31:12.151: D/dalvikvm(30222): GC_FOR_ALLOC freed 3640K, 63% free 18652K/49480K, paused 48ms, total 48ms
12-21 00:31:17.757: D/dalvikvm(30222): GC_FOR_ALLOC freed 3627K, 63% free 18669K/49480K, paused 93ms, total 93ms
12-21 00:31:24.014: D/dalvikvm(30222): GC_FOR_ALLOC freed 3641K, 63% free 18679K/49480K, paused 46ms, total 46ms
12-21 00:31:30.090: D/dalvikvm(30222): GC_FOR_ALLOC freed 3644K, 63% free 18688K/49480K, paused 48ms, total 48ms
12-21 00:31:35.536: D/dalvikvm(30222): GC_FOR_ALLOC freed 3639K, 63% free 18706K/49480K, paused 50ms, total 50ms
12-21 00:31:41.763: D/dalvikvm(30222): GC_FOR_ALLOC freed 3659K, 63% free 18711K/49480K, paused 52ms, total 53ms
12-21 00:31:47.439: D/dalvikvm(30222): GC_FOR_ALLOC freed 3652K, 63% free 18724K/49480K, paused 54ms, total 54ms
12-21 00:31:53.085: D/dalvikvm(30222): GC_FOR_ALLOC freed 3677K, 63% free 18716K/49480K, paused 38ms, total 38ms
12-21 00:31:59.882: D/dalvikvm(30222): GC_FOR_ALLOC freed 3672K, 63% free 18711K/49480K, paused 50ms, total 67ms
12-21 00:32:05.838: D/dalvikvm(30222): GC_FOR_ALLOC freed 3672K, 63% free 18704K/49480K, paused 42ms, total 42ms
12-21 00:32:13.276: D/dalvikvm(30222): GC_FOR_ALLOC freed 3680K, 63% free 18686K/49480K, paused 37ms, total 37ms

附:代码很长,不能发。

感谢您的帮助、提示或猜测。

【问题讨论】:

  • 这里奇怪的不是垃圾收集;这是您的应用程序的行为
  • @AndrewBarber 应用程序的行为有什么问题?为什么不是GC?
  • 你的第一段。这不是应用程序通常的行为方式。您似乎也不了解垃圾收集的工作原理或原因。可用内存并不像您想象的那么重要。
  • @AndrewBarber。但是有了这个空闲内存量,应该触发 GC_CONCURRENT。它应该比 GC_FOR_ALLOC 更快。
  • 重读后-分配和释放内存是造成这种情况的原因。最好的办法是在对象级别分配和重用。顺便说一下,分配一大块内存然后释放它会增加应用程序的堆,但不是很好。在 GC 运行之前,分配的内存仍将被分配。所以新声明的内存实际上不能被重用。这意味着您的算法将请求更多内存来获得可用内存。如果您实际上没有请求足够的空间来增长堆,那么这尤其糟糕,但实际上它是从预先分配的内存中填充的

标签: android memory-management garbage-collection


【解决方案1】:

为什么要触发 GC_FOR_ALLOC?有大量可用内存。

引用myself:

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

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

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

(细节比这更复杂,但这是一篇博文,不是论文)

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

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

您的GC_FOR_ALLOC 行表明您正在分配的内存在分配时没有可用的连续足够大的块,这迫使 Dalvik 必须通过 GC 来尝试释放内存,这样您就可以放松了一个足够大的块。

如果您想减少或删除您的GC_FOR_ALLOC 行,您将需要尝试确定您的大分配是多少。在这里,“大”是指单独的分配,因此高度复杂的对象图总体上可能是“大”的,但每个单独的对象都很小,因此不是问题。 byte[] 分配往往是罪魁祸首,至少对于位图处理应用程序而言。

【讨论】:

  • 非常感谢您的解释。在我的实现中,我确实分配了大量的小对象。可能,他们搞砸了堆
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多