老生常谈, 内存泄漏和优化是个古老的话题。 提高码农的经验和代码水平是根本, 外在的工具是辅助手段。 常见的手段包括Android原生的严苛模式、Eclipse公司提供的Java内存分析工具MAT、主流的三方库如LeakCanary, 该三板斧功夫是Android码农要掌握的基本本领。
简单的讲:
内存泄漏就是申请了资源,用完后没释放,并可能导致Out Of Memory问题;在Java语言中就是引用没释放(PS:Swift也是通过引用计数使用自动内存回收机制, 大道同源)。
内存优化就是尽量少占用内存,包括服务、线程、缓存、实例对象等所有耗费内存的东西。
在分析内存泄漏时,可以借助工具或命令查询进程内存的变化, 尤其要注意内存抖动的情况! 即内存忽上忽下。
1、建议在项目里添加如下代码, 作用是开发模式下执行严苛模式。 严苛模式可以在符合条件时让程序崩溃并输出日志,从而找到内存泄漏的地方。
在build.gradle文件里声明RUN_MODE变量:
android {
……
buildTypes {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
buildConfigField("String", "RUN_MODE", "\"dev\"")
………..
}
release {
buildConfigField("String", "RUN_MODE", "\"release\"")
………..
}
}
…..
}
在Application的派生类onCreate函数里添加下面代码://在AndroidManifest.xml里配置application属性
public class TheApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
…….. //初始三方库和一些全局变量
//在build.gradle文件里配置变量,仅在debug模式时运行严苛模式
if ("dev".equalsIgnoreCase(BuildConfig.RUN_MODE)) {
//严苛模式测试
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDeath()
.build());
}
}
…..
}
StrictMode检测到失败时程序崩溃并输出日志: 第一行art表示当前是art虚拟机, Android5.0以前是dalvik虚拟机。
08-18 11:22:14.799 E/art (21386): Invalid object in managed heap: 0x74615fe0
08-18 11:22:14.801 E/StrictMode(21386): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
08-18 11:22:14.801 E/StrictMode(21386): java.lang.Throwable: Explicit termination method 'release' not called
08-18 11:22:14.801 E/StrictMode(21386): at dalvik.system.CloseGuard.open(CloseGuard.java:180)
08-18 11:22:14.801 E/StrictMode(21386): at android.view.Surface.setNativeObjectLocked(Surface.java:459)
08-18 11:22:14.801 E/StrictMode(21386): at android.view.Surface.<init>(Surface.java:145)
08-18 11:22:14.801 E/StrictMode(21386): at com.eloancn.elfranchisee.camera.Camera2BasicFragment.createCameraPreviewSession(Camera2BasicFragment.java:769)
08-18 11:22:14.801 E/StrictMode(21386): at com.eloancn.elfranchisee.camera.Camera2BasicFragment.access$400(Camera2BasicFragment.java:84)
08-18 11:22:14.801 E/StrictMode(21386): at com.eloancn.elfranchisee.camera.Camera2BasicFragment$2.onOpened(Camera2BasicFragment.java:212)
08-18 11:22:14.801 E/StrictMode(21386): at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:134)
08-18 11:22:14.801 E/StrictMode(21386): at android.os.Handler.handleCallback(Handler.java:742)
08-18 11:22:14.801 E/StrictMode(21386): at android.os.Handler.dispatchMessage(Handler.java:95)
08-18 11:22:14.801 E/StrictMode(21386): at android.os.Looper.loop(Looper.java:154)
08-18 11:22:14.801 E/StrictMode(21386): at android.os.HandlerThread.run(HandlerThread.java:61)
08-18 11:22:14.801 W/System.err(21386): StrictMode VmPolicy violation with POLICY_DEATH; shutting down.
2、优秀的三方库,例如LeakCanary; 集成到项目中即可, 网上有很多介绍。 目前开发的项目没用到,所有没图
3、 MAT即Memory Analyze Tool, 下载地址:http://www.eclipse.org/mat/ 。 它是个不错的内存分析工具,已经存在很多年了,网上也有很多介绍,简单说明一下。
1、 先点击“开始”, 操作应用程序一段时间后点击“结束”, 导出hprof文件。
2、转换为MAT工具能识别的格式, android sdk里有hprof-conv文件, 在cmd窗口执行hprof-conf 源文件路径 目标文件路径 。
3、打开hprof文件, 下面几个选项都有英文说明, 就不解释了。 常用的是Histogram和Leak Suspects选项。 查看Histogram中的对象引用,判断它是否应该还缓存在内存中,做内存优化。
内存优化比内存泄漏是更高阶的话题, 取决于码农的知识面和技术深度, 工具只能测量出内存大小, 但无法告诉你怎么减少它。随便罗列几条:
1、使用适当的数据结构, 例如使用稀疏矩阵替换数组;
2、在时间空间转换上做适当取舍, 例如HashMap容量初值是16个, 可以给它赋其它初值;
3、三方库设置合理的参数;
4、高阶一点的就是内存对齐, 例如一个类里有10个成员变量, 调整先后顺序占用的内存空间是不同的。
优化无止境,且做且珍惜
以前总结的一些常用知识点: Android开发的一些知识点