Java
内存分配
静态存储区(方法区):
主要存放静态数据、全局static数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
栈区 (Stack):
当方法被执行时,方法体内的局部变量(包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。
堆区 (Heap):
又称动态内存分配,通常就是指直接 new 出来的对象实例或数组存放的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。
【GC回收的就是Heap中的内存】
public class Sample {
//s1/mSample1在堆区
int s1 = 0;
Sample mSample1 = new Sample();
public void method() {
//s2、arr2、mSample2在栈区,对应的引用对象在堆区
int s2 = 1;
int[] arr2 = {1,2,3}
Sample mSample2 = new Sample();
}
}
Sample mSample3 = new Sample();
mSample3.method();
回收机制
内存泄露是指该内存空间使用完毕之后未回收;
垃圾回收(Garbage Collection,GC)
判断是否为垃圾
引用计数法
- 为每一个创建的对象分配一个引用计数器,用来存储该对象被引用的个数。当该个数为零,意味着没有人再使用这个对象,可以认为“对象死亡”。
- 这种方案存在严重的问题,就是无法检测“循环引用”:当两个对象互相引用,即使它俩都不被外界任何东西引用,但它俩的计数都不为零,因此永远不会被回收。
- 因此,Java 里没有采用这样的方案来判定对象的“存活性”。
可达性分析算法
- 这种方案是目前主流语言里采用的对象存活性判断方案。
- 基本思路是把所有引用的对象想象成一棵树,从树的根结点 GC Roots 出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。其余的对象则被视为“死亡”的“不可达”对象,或称“垃圾”;
- java中可作为GC Root的对象有
- 虚拟机栈中引用的对象(本地变量表)
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象(Native对象)
回收算法
仅做了解,不细致探究;
垃圾回收机制是算法,在Java之前就有,JVM也未声明具体用哪种算法;
标记-清除算法
标记-整理算法
复制算法
分代收集算法
年轻代、年老代、持久代
引用类型
强引用(StrongReference):
JVM 宁可抛出 OOM ,也不会让 GC 回收;
软引用(SoftReference):
只有在内存空间不足时,才会被回收;
弱引用(WeakReference):
在 GC 时,一旦发现有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存;
虚引用(PhantomReference):
任何时候都可以被GC回收
Android 内存优化
Android 内存管理机制
共享内存
- Android应用的进程都是从一个叫做Zygote的进程fork出来的。Zygote进程在系统启动并且载入通用的framework的代码与资源之后开始启动。这使得大多数的RAM pages被用来分配给framework的代码,同时使得RAM资源能够在应用的所有进程之间进行共享。
- 大多数static的数据被mmapped到一个进程中。
- 大多数情况下,Android通过显式的分配共享内存区域(例如ashmem或者gralloc)来实现动态RAM区域能够在不同进程之间进行共享的机制。
分配与回收内存
限制应用的内存
- ActivityManager.getMemoryClass()可以用来查询当前应用的Heap Size阈值
应用切换操作
- Android系统并不会在用户切换应用的时候做交换内存的操作。Android会把那些不包含Foreground组件的应用进程放到LRU Cache中。
- 当系统开始进入Low Memory的状态时,它会由系统根据LRU的规则与应用的优先级,内存占用情况以及其他因素的影响综合评估之后决定是否被杀掉。
内存优化的几个方面
内存抖动
内存泄漏
内存溢出
线上、线下的检查和解决方案
一、内存抖动
短时间内大量的对象被创建又马上被释放;
虽然不会内存泄漏,但可能触发GC操作,影响帧率;
原因
- for循环创建大量对象
- onDraw创建大量对象(经常重新绘制)
- string大量拼接
解决
- 复用对象
- string --> StringBuilder
二、内存泄漏 ML
无用
GC Root可达
常见原因
- 静态变量:单例、静态属性引用...
- 内部类:Handler、Thread...
- 资源对象使用后未关闭: cursor、Bitmap、BraodcastReceiver、ContentObserver...
Android造成内存泄漏的场景
static类/变量
- 原因
- 单例的静态特性导致其生命周期和应用一样长
- 单例模式、静态类...
- 解决:
- 如需传入context,尽量使用ApplicationContext
- 软引用
private static ScrollHelper mInstance;
private ScrollHelper() {}
public static ScrollHelper getInstance() {
if (mInstance == null) {
synchronized (ScrollHelper.class) {
if (mInstance == null) {
mInstance = new ScrollHelper();
}
}
}
return mInstance;
}
private WeakReference<View> mScrolledViewWeakRef = null;
public void setScrolledView(View scrolledView) {
mScrolledViewWeakRef = new WeakReference<View>(scrolledView);
}
内部类
- 原因:
- 内部类持有外部类引用
- Handler、Runnable、AsyncTask...
- 解决:
- Static + WeakReference
- dostory的时候释放
WeakReference??
如果要访问Activity,虽然是没办法需要传入引用
但是随时GC啊,如果还在用这个handler那弱引用被回收了的呀???
软引用GC是内容够用不回收,但是Android2.3以后版本中,系统会优先将SoftReference的对象提前回收掉, 即使内存够用,其他和Java中是一样的。所以谷歌官方建议用LruCache(least recentlly use 最少最近使用算法)。
private static class MyHandler extends Handler{
private final WeakReference<Activity> mAct;
public MyHandler(TestActivity activity){
this.mAct = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
TestActivity testAct = mAct.get();
if (testAct != null) {
testAct.tvMsg.setText("this is msg");
}
}
}
Handler引起的内存泄漏:
msg.target指向handler,如果任务延迟或等待过长,可能会引起内存泄漏。
注册监听器的泄漏
- 使用ApplicationContext代替ActivityContext;
- 在Activity执行onDestory时,调用反注册;
资源操作没有及时回收
Cursor,Stream没有close,View没有recyle
集合中对象没清理造成的内存泄漏
我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
WebView
- 当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其占用的内存长期也不能被回收,从而造成内存泄露。
- 解决方案: 为webView开启另外一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。
三、内存溢出 OOM
什么情况下会发生OOM?
- 不同的版本、设备表现不同,有事Heap值明显高于ActivityManager.getMemoryClass()也不会发生OOM
如何避免OOM?
减小对象的内存占用
内存对象的重复利用
避免对象的内存泄露
内存使用策略优化
减小对象的内存占用
- 使用更加轻量的数据结构
- ArrayMap/SparseArray 代替 HashMap
- StringBuilder 代替 String 进行字符串操作
- 避免在Android里面使用Enum
- 减小Bitmap对象的内存占用
- inSampleSize:缩放比例
- decode format:解码格式
- 使用更小的图片
- 不同的资源包
- SVG
内存对象的重复利用
- 复用系统自带的资源
- 系统自带color、drawable
- RecyclerView优化
- Bitmap对象的复用
- inBitmap复用
- onDraw、listeners、StringBuilder
避免对象的内存泄露
- Activity、listener、register、handler...
- webview泄漏
- cursor、bitmap未及时关闭、回收
内存使用策略优化
- 慎用 large heap
- 设计合适的缓存大小
- 资源文件需要选择合适的文件夹进行存放
- Try catch某些大内存分配的操作
- 谨慎使用static对象
- 珍惜Services资源
- 优化布局层次,减少内存消耗
- 软引用:
- Java上按照强、软、弱、虚引用的顺序回收
- Android上回首先回收软引用,其它顺序相同