内存优化

一、JVM内存模型与分布

我们都知道JVM运行时的数据区域分为5大块

image.png
  • 1.程序计数器:虚拟机字节码的行号记录器,占用内存非常小,可以不用考虑。
  • 2.虚拟机栈:这个区域描述的是java方法执行的内存模型,每个方法代表一个栈帧,方法内大部分定义的变量都是栈里面的成员,他是有生命周期的,伴随着方法执行结束而销毁。
  • 3.本地方法栈:同虚拟机栈很相似,但他是Native层的。
  • 4.堆:几乎所有对象都在这个区域产生,该区域属于线程共享区域,所以线程销毁时,需要注意对象的销毁
  • 5.方法区::主要存储虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码等数据。

二、内存限制

android是基于Linux系统的,android中的进程分为两种:

  • 1.native进程:采用C/C++实现,不包含dalvik实例的linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的
  • 2.java进程:实例化了dalvik虚拟机实例的linux进程,进程的入口main函数为java函数。dalvik虚拟机实例的宿主进程是fork()系统调用创建的linux进程,所以每一个android上的java进程实际上就是一个linux进程,只是进程中多了一个dalvik虚拟机实例
    我们知道,操作系统对进程的内存是有限制的,而且操作系统对dalvik虚拟机自身的堆内存大小也是有限制的。可以通过如下命令查看限制大小:
adb shell getprop | grep dalvik.vm.heapgrowthlimit

可以在Androidmanifest文件中application节点加入android:largeHeap=“true”来增加其dalvik虚拟机中堆的大小
我们常说的堆大小其实是包涵两部分的,一是java的堆,而是native的堆,java堆中主要是一下java对象,由 C/C++申请的内存空间则在native堆中,也有一些对象需要结合native和java堆共同完成,比如bitmap,bitmap分为bitmap对象和其中存储的像素值,对象分配在java堆,而存储的像素值则根据版本不同存储的位置也不同,api 11 - api 25是存储在java堆中的,其他版本是存储在native堆中的

三、内存泄漏场景

1.静态引用

静态对象非法持有Activity上下文

2.匿名内部类,或非静态内部类

Handler,AsyncTask,TimerTask等,一般在处理多线程任务的时候

3.监听器

在Activity OnResume里面registListener,在OnPause一定要记得unregistListener

4.资源对象

BraodcastReceiver, ContentObserver,File,Cursor,Stream,Bitmap,Socket等资源,使用后未关闭会导致内存泄漏。这些对象JVM中有的只保存引用,而真实的数据存在JVM之外的内存,所以并非对象制为null即可,必须 调用 recycle, release, close等方法,清空内存。

4.WebView

在Activity的onDestory中调用 WebView的 clearHistory, clearView,destroy方法即可。

四、如何排查以及修复

1.LeakCanary
2.Android Studio 3.0 的 Android Profiler 分析器

五、如何进一步进行优化

  • 1.资源优化:

1.shape代替图片资源
2.部分png图改jpg图,比如启动页
3.gradle配置shrinkResources属性,删除无效资源

  • 2.代码优化

1.删除冗余代码,减少编译带来的内存开销
2.第三方库so包的缩小, 能满足需求的情况下,尽量实用阉割版