Android内存性能优化(内部资料总结)

简介: 刚入门的童鞋肯能都会有一个疑问,Java不是有虚拟机了么,内存会自动化管理,我们就不必要手动的释放资源了,反正系统会给我们完成。其实Java中没有指针的概念,但是指针的使用方式依然存在,一味的依赖系统的gc,很容易就造成了内存的浪费。

 

刚入门的童鞋肯能都会有一个疑问,Java不是有虚拟机了么,内存会自动化管理,我们就不必要手动的释放资源了,反正系统会给我们完成。其实Java中没有指针的概念,但是指针的使用方式依然存在,一味的依赖系统的gc,很容易就造成了内存的浪费。

 

Java基于垃圾回收的内存机制

Java的内存管理机制会自动回收无用对象所占用的内存,减轻手工管理内存的负担

      1、C/C++: 从申请、使用、释放都需要手工管理

      2、Java:无用的对象的内存会被自动回收

 

 

什么样的对象是无用的对象

      1、Java通过引用来操作一个具体的对象,引用类似于C 中的指针。一个对象可以持有其他对象的引用。

      2、从一组根对象(GC Roots)开始,按对象之前的引用关系遍历所有对象,在遍历过程中标记所有的可达对象。如果一个对象由根对象出发不可达,则将它作为垃圾收集。


 

GCRoot 都有哪些?

1、  Class:由系统的类加载器加载的类对象

2、  Static Fields

3、  Thread:活着的线程

4、  Stack Local: java方法的局部变量或参数

5、  JNI Local: JNI方法中的局部引用

6、  JNI Global: 全局的JNI引用

7、  Monitor used: 用于同步的监控对象

8、Help by VM: 用于JVM特殊目的由GC保留的对象


 


Java程序中的内存泄漏

对象的内存在分配之后无法通过程序的执行逻辑释放对该对象的引用,不能被回收该对象所占内存

内存泄漏的危害

1、  引起OutOfMemoryError

2、  内存占用高时JVM虚拟机会频繁触发GC, 影响程序响应速度

3、内存占用大的程序容易被各种清理优化程序中止,用户也更倾向于卸载这些程序

 

 

Android应用的开发语言为Java,每个应用最大可使用的堆内存受到Android系统的限制

Android每一个应用的堆内存大小有限

      1、  通常的情况为16M-48M

      2、  通过ActivityManager的getMemoryClass()来查询可用堆内存限制

      3、3.0(HoneyComb)以上的版本可以通过largeHeap=“true”来申请更多的堆内存

           Nexus S(4.2.1):normal 192, largeHeap 512

      4、如果试图申请的内存大于当前余下的堆内存就会引发OutOfMemoryError()

      5、应用程序由于各方面的限制,需要注意减少内存占用,避免出现内存泄漏。

 

 

用MAT工具来检测内存泄漏

在试图窗口中新建一个Memory Analysis会出现一个

没有的可以去http://www.eclipse.org/mat/downloads.php安装一下MAT

 

在Android 的调试环境DDMS下,找到Heap dump

Dump下当前内存中的镜像文件,*****.hprof



能清楚的看到每一个部分暂用的内存大小。

也可以切换试图,group查看不同包不同类的占用细节。


Heap dump

•       包含了触发Heap dump生成的时刻Java进程的内存快照,主要内容为各个Java类和对象在堆内存中的分配情况

Memory Analyzer Tool (MAT)

 

 

 

常见内存泄露原因

 

Context对象泄漏

      1、如果一个类持有Context对象的强引用,就需要检查其生存周期是否比Context对象更长。否则就可能发生Context泄漏。

      2、View持有其创建所在Context对象的引用,如果将View对象传递给其它生存周期比View所在Context更长的强引用,就可能会引起内存泄漏。

例如View#setTag(int, Object)的内存泄漏https://code.google.com/p/android/issues/detail?id=18273

      3、把Context对象赋给static变量。

 

 

避免Context对象泄漏Checklist

      1、检查所有持有对Context对象强引用的对象的生命周期是否超出其所持有的Context对象的生命周期。

      2、检查有没有把View传出到View所在Context之外的地方,如果有的话就需要检查生命周期。

      3、工具类中最好不要有Context成员变量,尽量在调用函数时直接通过调用参数传入。如果必须有Context成员变量时,可以考虑使用WeakReference来引用Context对象。

      4、View持有其创建所在Context对象的引用,如果将View对象传递给其它生存周期比View所在Context更长的强引用,就可能会引起内存泄漏。

      5、  检查把Context或者View对象赋给static变量的地方,看是否有Context泄漏。

      6、检查所有把View放入容器类的地方(特别是static容器类),看是否有内存泄漏。7、使用WeakHashMap也需要注意有没有value-key的引用。

      7、尽量使用ApplicationContext。

 

Handler对象泄漏

 

1、发送到Handler的Message实际上是加入到了主线程的消息队列等待处理,每一个Message持有其目标Handler的强引用。

如我们通常使用的匿名内部类Handler


[java] view plaincopyprint? 



  • <span style="font-size:18px;">HandlermHandler = new Handler() {  
  •     @Override  
  •     public voidhandleMessage(Message msg) {  
  •        mImageView.setImageBitmap(mBitmap);  
  •     }  
  • }</span>  




上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用,因为View会依附着一个Activity。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

当然,应为是Handler对外部持有引用的原因,我们就可以将Activity设置为一个弱引用,在不必要的时候,不再执行内部方法。


[java] view plaincopyprint? 



  • <span style="font-size:18px;">/**
  • * @author zhoushengtao
  • @since 2013-12-16 下午3:25:36
  • */  
  •    
  • import android.app.Activity;  
  • importandroid.content.Context;  
  • importandroid.os.Handler;  
  • importandroid.os.Message;  
  •    
  • importjava.lang.ref.WeakReference;  
  •    
  • publicclass WeakRefHandler extends Handler  
  • {  
  •     WeakReference<Context> mWeakContext;  
  •    
  •     public WeakRefHandler(Context context)  
  •     {  
  •         mWeakContext = newWeakReference<Context>(context);  
  •     }  
  •    
  •     @Override  
  •     public void handleMessage(Message msg)  
  •     {  
  •         if((mWeakContext.get() instanceofActivity )&& ((Activity)mWeakContext.get()).isFinishing())  
  •                 return ;  
  •         if(mWeakContext==null){  
  •             return ;  
  •         }  
  •         super.handleMessage(msg);  
  •     }  
  • }</span>  




2、Non-staticinner class 和anonymous class持有其outer class的引用。

 

 

 

 

 

Drawable.Callback引起的内存泄漏

Drawable对象持有Drawable.callback的引用。当把一个Drawable对象设置到一个View时,Drawable对象会持有该View的引用作为Drawable.Callback



避免Drawable.Callback引起内存泄漏

•       尽量不要在static成员中保存Drawable对象

•       对于需要保存的Drawable对象, 在需要时调用Drawable#setCallback(null).

 

其他内存泄漏

      1、Android DigitalClock引起的内存泄漏http://code.google.com/p/android/issues/detail?id=17015

      2、使用Map容器类时,作为Key 的类没有正确的实现hashCode和equal函数


 

 

其他内存泄漏

•       JNI程序中的内存泄漏

1、  Malloc/free。

2、  JNI Global reference

•       Thread-Local Variable

1、  相当于Thread对象的成员变量, 可以存储线程相关的状态

2、  如果thread是alive状态,那么Thread-Local中的对象就无法被GC。

 

进程内存占用监测工具

Dumpsys

•       $ dumpsys meminfo [pid]


 

 

Procrank + Shell脚本

•       #procrank

      1、  VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

      2、  RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)

3、  PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

4、  USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)


Shell脚本

#!/bin/bash

while true; do

         adbshell procrank | grep "com.qihoo360.mobilesafe"

         sleep1

done

 

当然,部分机型的sh都是经过第三方手机商精简过的,很多命令都用不了。Procrank,就是一个经常被精简掉的命令。

         鉴于此:

         自己写了一个小工具,检测内存的实时变化,

Github地址:https://github.com/stchou/JasonTest


小结

      1.      保存对象前要三思

                               I.           对象本身有无隐含的引用

                             II.           保存后何时能够回收

      2.      要了解常见的隐含引用

                               I.           anonymous class outer class

                             II.           View to context

      3.      要通过各种工具检查内存占用是否有异常

      4.      创建大对象时,要检查它的生命周期

摘自:http://www.eoeandroid.com/thread-334987-1-1.html

相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
257 4
|
3月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
85 2
|
3月前
|
存储 前端开发 Java
Android MVVM架构模式下如何避免内存泄漏
Android采用MVVM架构开发项目,如何避免内存泄漏风险?怎样避免内存泄漏?
129 1
|
1月前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
2月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
98 16
|
1月前
|
网络协议 Linux Android开发
深入探索Android系统架构与性能优化
本文旨在为读者提供一个全面的视角,以理解Android系统的架构及其关键组件。我们将探讨Android的发展历程、核心特性以及如何通过有效的策略来提升应用的性能和用户体验。本文不包含常规的技术细节,而是聚焦于系统架构层面的深入分析,以及针对开发者的实际优化建议。
70 1
|
2月前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
2月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
48 5
|
2月前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
74 3
|
2月前
|
存储 缓存 监控

热门文章

最新文章