Android最佳性能实践(二)

转载请注明出处:

由于Android是为移动设备开发的操作系统,我们在开发应用程序的时候应当始终把内存问题充分考虑在内。虽然Android系统拥有垃圾自动回收机制,但这并不意味着我们就可以完全忽略何时去分配或释放内存。即使我们全部按照上一篇文章中给出的编程建议来去编写程序,还是会很有可能出现内存泄露或其它类型的内存问题。所以,唯一能够解决问题的办法,就是尝试去分析应用程序的内存使用情况,那么本篇文章就会教大家如何进行分析。如果你还没有看过前面一篇文章,建议先去阅读 Android最佳性能实践(一)——合理管理内存。

虽说现在的手机内存都已经非常大了,但是我们大家都知道,系统是不可能将所有的内存都分配给我们的应用程序的。没错,每个程序都会有可使用的内存上限,这被称为堆大小(Heap Size)。不同的手机,堆大小也不尽相同,随着现在硬件设备不断提高,堆大小也已经由Nexus One时的32MB,变成了Nexus 5时的192MB。如果大家想要知道自己手机的堆大小是多少,可以调用如下代码:

ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);int heapSize = manager.getLargeMemoryClass();结果是以MB为单位进行返回的,我们在开发应用程序时所使用的内存不能超出这个限制,否则就会出现OutOfMemoryError。因此,比如说我们的程序中需要缓存一些数据,就可以根据堆大小来决定缓存数据的容量。

下面我们来讨论一下Android的GC操作,GC全称是Garbage Collection,也就是所谓的垃圾回收。Android系统会在适当的时机触发GC操作,一旦进行GC操作,就会将一些不再使用的对象进行回收。那么哪些对象会被认为是不再使用,并且可以被回收的呢?我们来看下面一张图:

上图当中,每个蓝色的圆圈就代表一个内存当中的对象,而圆圈之间的箭头就是它们的引用关系。这些对象有些是处于活动状态的,而有些就已经不再被使用了。那么GC操作会从一个叫作Roots的对象开始检查,所有它可以访问到的对象就说明还在使用当中,应该进行保留,而其它的对象就表示已经不再被使用了,如下图所示:

可以看到,目前所有黄色的对象仍然会被系统继续保留,而蓝色的对象就会在GC操作当中被系统回收掉了,这大概就是Android系统一次简单的GC流程。

那么什么时候会触发GC操作呢?这个通常都是由系统去决定的,我们一般情况下都不需要主动通知系统应该去GC了(虽然我们确实可以这么做,下面会讲到),但是我们仍然可以去监听系统的GC过程,以此来分析我们应用程序当前的内存状态。那么怎样才能去监听系统的GC过程呢?其实非常简单,系统每进行一次GC操作时,都会在LogCat中打印一条日志,我们只要去分析这条日志就可以了,日志的基本格式如下所示:

D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <Pause_time>注意这里我仍然是以dalvik虚拟机来进行说明,art情况下打印的内容也是基本类似的。。

首先第一部分GC_Reason,这个是触发这次GC操作的原因,一般情况下一共有以下几种触发GC操作的原因:

接下来第二部分Amount_freed,表示系统通过这次GC操作释放了多少内存。

然后Heap_stats中会显示当前内存的空闲比例以及使用情况(活动对象所占内存 / 当前程序总内存)。

最后Pause_time表示这次GC操作导致应用程序暂停的时间。关于这个暂停的时间,Android在2.3的版本当中进行过一次优化,在2.3之前GC操作是不能并发进行的,也就是系统正在进行GC,那么应用程序就只能阻塞住等待GC结束。虽说这个阻塞的过程并不会很长,也就是几百毫秒,但是用户在使用我们的程序时还是有可能会感觉到略微的卡顿。而自2.3之后,GC操作改成了并发的方式进行,就是说GC的过程中不会影响到应用程序的正常运行,但是在GC操作的开始和结束的时候会短暂阻塞一段时间,不过优化到这种程度,用户已经是完全无法察觉到了。

下面是一次GC操作在LogCat中打印的日志:

可以看出,和我们上面所介绍的格式是完全一致的,最后的暂停时间31ms+7ms,一次就是GC开始时的暂停时间,一次是结束时的暂停时间。另外可以根据进程id来区分这是哪个程序中进行的GC操作,那么从上图就可以看出这条GC日志是属于24699这个程序的。

那么这是使用dalvik运行环境时所打印的GC日志,而自Android 4.4版本之后加入了art运行环境,在art中打印GC日志基本和dalvik是相同的,如下图所示:

相信没有什么难理解的地方吧,art中只是内容显示的格式有了稍许变化,打印的主体内容仍然是不变的。

好的,通过日志的方式我们可以简单了解到系统的GC工作情况,但是如果我们想要更加清楚地实时知晓当前应用程序的内存使用情况,,只通过日志就有些力不从心了,我们需要通过DDMS中提供的工具来实现。

打开DDMS界面,在左侧面板中选择你要观察的应用程序进程,然后点击Update Heap按钮,接着在右侧面板中点击Heap标签,之后不停地点击Cause GC按钮来实时地观察应用程序内存的使用情况即可,如下图所示:

接着继续操作我们的应用程序,然后继续点击Cause GC按钮,如果你发现反复操作某一功能会导致应用程序内存持续增高而不会下降的话,那么就说明这里很有可能发生内存泄漏了。

好了,讨论完了GC,接下来我们讨论一下Android中内存泄漏的问题。大家需要知道的是,Android中的垃圾回收机制并不能防止内存泄漏的出现,导致内存泄漏最主要的原因就是某些长存对象持有了一些其它应该被回收的对象的引用,导致垃圾回收器无法去回收掉这些对象,那也就出现内存泄漏了。比如说像Activity这样的系统组件,它又会包含很多的控件甚至是图片,如果它无法被垃圾回收器回收掉的话,那就算是比较严重的内存泄漏情况了。

人要有梦想,有了梦想才会努力奋斗,

Android最佳性能实践(二)

相关文章:

你感兴趣的文章:

标签云: