安卓(Android):为什么启动新活动(Activity)需要这么长时间?

3
我有一个名为BrowseActivity的Android活动,它从数据库中加载大约3000条记录(每条记录约100字节)到内存中,并在ListView中显示它们。在AVD中加载需要3到6秒,这很好。
问题出现在我想要从BrowseActivity中启动一个新的活动时。即使只是打开最琐碎的活动,也需要大约30秒。Dalvik VM似乎因未知原因执行了大量垃圾回收。请查看下面的logcat输出。
01-30 02:06:27.025: D/dalvikvm(553): GC_FOR_MALLOC freed 3889 objects / 221200 bytes in 45ms
01-30 02:06:28.234: D/dalvikvm(553): GC_FOR_MALLOC freed 2784 objects / 76864 bytes in 48ms
01-30 02:06:29.614: D/dalvikvm(553): GC_FOR_MALLOC freed 2665 objects / 70088 bytes in 64ms
01-30 02:06:30.915: D/BrowseActivity(553): Low memory status: false
01-30 02:06:30.915: D/BrowseActivity(553): Low memory threshold (KB): 16384
01-30 02:06:30.915: D/BrowseActivity(553): Memory available (KB): 451348
01-30 02:06:31.095: D/dalvikvm(553): GC_EXTERNAL_ALLOC freed 2711 objects / 85904 bytes in 62ms
01-30 02:06:49.705: D/dalvikvm(553): GC_FOR_MALLOC freed 8894 objects / 600272 bytes in 89ms
01-30 02:06:50.414: D/dalvikvm(553): GC_FOR_MALLOC freed 10757 objects / 730720 bytes in 68ms
01-30 02:06:51.105: D/dalvikvm(553): GC_FOR_MALLOC freed 10251 objects / 694864 bytes in 67ms
... and about 30 more garbage collection messages

我已经尝试过较少的记录数量:1和300。这会导致更少的垃圾收集消息(分别为1和7),以及更快的活动加载速度(分别为1和6秒),但可用内存的差异相当微小。
01-30 02:03:35.295: D/BrowseActivity(491): Low memory status: false
01-30 02:03:35.295: D/BrowseActivity(491): Low memory threshold (KB): 16384
01-30 02:03:35.295: D/BrowseActivity(491): Memory available (KB): 453184

01-30 02:04:53.494: D/BrowseActivity(522): Low memory status: false
01-30 02:04:53.494: D/BrowseActivity(522): Low memory threshold (KB): 16384
01-30 02:04:53.494: D/BrowseActivity(522): Memory available (KB): 453152

似乎缓慢的活动加载时间是由GC开销引起的。我的问题是:为什么Dalvik VM会进行大量的GC呢?即使有3000条记录,我相信它只会占用不到500 KB的内存。如果不是GC开销的问题,还有哪些可能导致这种缓慢的活动加载呢?
顺便说一句,我使用内置类ActivityManager.MemoryInfo提供的信息记录了内存可用性。

1
你在切换之前调用finalize了吗?这显然会启动GC http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle --- 生命周期对于我所知道的GC和内存管理并没有说太多。 - ddoor
加载3到6秒对于3k条记录来说慢吗? - wtsang02
@FaddishWorm 不,我没有调用finalize。 - Andree
考虑到这是一个AVD,我认为它还可以。实际上,在真实的硬件上运行会更快。 - Andree
我通过避免创建Activity并使用DialogFragment来解决了我的问题。然而,我仍然保持问题的开放状态,因为神秘的GC爆发问题尚未解决,其他人可能能够提供答案。 - Andree
1个回答

1
我认为您没有对拥有3000条记录的Activity进行引用,这会导致在更改活动时激活GC。
如果您使用MVC类型模式,并且控制器具有对每个Activity的引用,则不认为GC会清理这些记录。当存在一部分内存但没有已知引用时,GC会启动,一旦切换活动,对活动及其所有函数和成员变量的引用将不再使用,因此应该进行清理。
GC的行为方式也可能取决于硬件,即如果系统可用的内存很多,则可能与没有可用内存时不同。
我有一个加载了超过3000条记录的应用程序,控制器对每个活动都有引用,因此列表视图适配器中的数据不会被清除。
至于开销,这不一定是GC - 但看起来它会导致开销增加。唯一的其他贡献因素将是渲染屏幕组件等所需的时间。
希望这有所帮助!
~ Dan

嗨,丹。谢谢您的答复!您使用控制器来保留对每个活动的引用以防止它们被垃圾回收的做法很有趣。然而,据我所知,对于每个活动的引用已经由Android的“后退栈”维护,只有当活动从此后退栈中移除(即销毁)时才会被GC回收? - Andree
原生的Android组件是否会调用finalize或实现自己的GC?由于它们为您管理活动,我认为它们正在控制它。如果Android默认维护对每个活动的引用,那么就会有一些非常泄漏的应用程序存在。 - ddoor
你如何实现这个控制器类?我猜它只是一个带有 List<Activity> 属性的单例类? - Andree
你可以按照自己的喜好来做,但那听起来应该是可行的。只要你保持对它的引用。如果你想要一个标识符,你可能需要一个HashMap<String, Activity>。 - ddoor
控制器方法无效,因为前一个活动中的3000条记录显然没有被垃圾回收(如果我回到BrowseActivity,仍然可以浏览3000个项目而不需要重新加载)。但是我非常感激您的建议。 - Andree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接