(一)内存抖动:
短时间内有大量对象创建销毁,它伴随着频繁的GC;
比较典型的就是字符串拼接造成的内存抖动。 例如:我们通常会这么做字符串拼接操作
private String addStr(String[] values) {
String result = "";
for (int i = 0; i < values.length;i++){
result += i;
}
return result;
}
这里我们使用一个插件ASM bytecode Viewer 反编译即可看到对应的字节码指令,可以看到这段代码进行字符串拼接操作时会不断的创建StringBuilder对象 也就是说当values的长度为1000时会创建1000次的StringBuilder对象,而这些对象对于我们来说,都是零时产生的对象,这种情况就极可能造成内存抖动。此处我们可以自己创建一个StringBuilder对象,代码如下:
public String addStr(String[] values) {
StringBuilder result = new StringBuilder();
// 10000
for (int i = 0; i < values.length; i++) {
result.append(result).append(values[i]);
}
return result.toString();
}
这样我们就只创建了一次StringBuilder,极大的减少了零时对象的创建,避免内存抖动问题。
注:Android里面使用的CMS垃圾回收器, 内存抖动会造成的结果,
1.卡顿:
当我们的App不断产生新的对象时,App所消耗的内存就越来越多,当内存占用到一定的程度的时候,系统就会使用GC进行垃圾回收,而当进行GC回收的时候,应用的主线程,或者其他线程就会被挂起,从而导致用户的操作得不到及时的反馈,引起卡顿。
2.OOM:
CMS垃圾回收器老年代采用标记清除算法,这种在回收之后会产生大量的内存碎片,内存不连续,此时去申请内存时就可能产生OOM。
注:CMS 新生代采用的复制算法,不会产生大量的内存碎片。
预防抖动:
*避免在循环中创建对象
*避免在频繁调用的方法中创建对象
*允许复用的情况下,使用对象池进行缓存,如: Handler的Message单链表(obtain);
(二)内存泄漏
程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费。长生命周期对象的强引用,从而导致短生命周期对象无法被回收。
可达性分析法 通过一系列称为"GC ROOTS"的对象作为起始点,从这些节点向下搜索,搜索所有的引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
这里我们需要知道java中的四种引用方式:
强引用:是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它,
软引用(SoftReference):定义一些还有用但非必须的对象。对于软引用关联的对象,GC不会直接回收,而是在系统将要内存溢出之前才会触发GC将这些对象进行回收。
弱引用:同样定义非必须对象,被弱引用关联的对象在 GC执行时会被直接回收。
虚引用(PhantomReference): 与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
通常我们在使用Handler时,会使用如下方式:
mHandler = new Handler();
mHandler.postDelayed(new Runnable(){
public void run(){
}
},1000);
此处容易发生内存泄漏,因为 new Runnable()是匿名内部类持有activity的引用。
优化:
方式一: 在onDestroy()方法中调用: mhandler.removeCallbackAndMessage(null);
方式二: 将匿名内部类改为静态内部类:
static class R1 implements Runnable {
WeakReference<SecondActivity> activity;
public R1(SecondActivity activity) {
this.activity = new WeakReference<SecondActivity>(activity);
}
@Override
public void run() {
System.out.println(activity.get().i);
}
}
Handler mHandler;
使用:mHandler.postDelayed(new R1(this), 10000
这里采用的是弱引用,当GC回收时,会将activity回收掉。
内存问题常见的场景:
*集合类: 当使用集合时,只有添加元素,没有对应的删除元素 如EventBus 只有注册没有注销!
*静态成员/单例 作为GC ROOT,持有短生命周期引用(如Activity)导致 其短生命周期对象无法释放。
*未关闭/释放资源 如FileOutputStream 未Close。
*非静态内部类 如Handler postDelayed一个匿名Runnable,退出actiivty时消息没处理完。
*系统问题 WebView 、InputMethodManager等;