查看App内存限制:需要root
adb shell cat /system/build.prop
dalvik.vm.heapstartsize=6m app启动的初始分配内存
dalvik.vm.heapgrowthlinit=128m app最大内存限制
dalvik.vm.heapsize=192m 开启largeHeap="true"
内存溢出:out of momery
是指程序在申请内存时,没有足够的内存空间给其使用,导致OOM异常
产生OOM原因:
Java堆内存溢出:OutOfMemoeryError
Java中被创建的对象实例,所占用的内存空间过大,超出了当前应用进程能获取的最大内存空间。
无足够的连续内存空间
FD数量超出限制 --1024
线程数量超出限制 --1024
虚拟内存不足
内存泄漏:momery leak
是指程序在申请内存后,被某个对象一直持有而无法释放已申请的内存空间。
一次的内存泄漏的危害可以忽略,但是内存泄漏堆积会很严重,无论多少内存,迟早会被耗光,最终导致内存溢出
产生内存泄漏的原因:
单例造成的内存泄漏
产生原因:
由于单例模式的静态特性,使得它的生命周期和我们的应用一样长,如果让单例无限制的持有Activity的强引用就会导致内存泄漏
解决方案:
使用Activity的弱引用,或者没特殊需求时使用Application Context
//单例造成的内存泄漏
public class SingletonContext {
private static SingletonContext instance;
private Context mContext;
public SingletonContext(Context context) {
this.mContext = context;
}
public static SingletonContext getInstance(Context context) {
if (instance == null){
instance = new SingletonContext(context);
}
return instance;
}
}
非静态内部类创建静态实例造成的内存泄漏
产生原因:
非静态内部类持有外部类实例的引用,若非静态内部类的实例是静态的,便拥有app存活期整个生命周期,长期持有外部类的引用,阻止外部类实例被回收。
解决方案:
改为静态内部类,不再持有外部类实例的引用
避免申明非静态内部类的静态实例
将内部类抽取出来封装成一个单例,如需使用Context,没有特殊要求就使用Application Context;如果需要Activity Context,则使用完毕后置空,或者使用弱引用
//非静态内部类创建静态实例造成的内存泄漏
public class StaticLeakActivity extends AppCompatActivity {
//创建静态实例
private static NoneStaticClass mResource = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mResource == null) {
mResource = new NoneStaticClass();
}
}
//非静态内部类
private class NoneStaticClass {
}
}
handler造成的内存泄漏
产生原因:
非静态Handler持有Activity或Service的引用,Message中的target指向Handler实例,所以当Message在MessageQueue中排队,长时间未得到处理时,Activity边不会被回收,导致临时性内存泄露。
解决方案:
静态内部类+弱引用:
将handler声明成静态内部类,通过弱引用(weakReference)的方式持有Activity
在Activity销毁时,清空Handler中未执行或正在执行的Callback以及Message:
mHandler.removeCallbacksAndMessages(null);
//handler造成的内存泄露
public class HandlerLeakActivity extends AppCompatActivity {
//1.匿名内部类
private final Handler mLeakHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//延迟操作
mLeakHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 50);
}
//2,非静态内部类
private class MyHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
private MyHandler mHandler = new MyHandler();
//解决方案:静态内部类+弱引用
private static class FinalHandler extends Handler {
//弱引用,在垃圾回收时,activity可被回收
private WeakReference<HandlerLeakActivity> mWeakReference;
public FinalHandler(HandlerLeakActivity activity) {
this.mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
//在Activity销毁时,可清空Handler中未执行或正在执行的Callback以及Message:
@Override
protected void onDestroy() {
super.onDestroy();
//清空handler管道和队列
mHandler.removeCallbacksAndMessages(null);
}
}
线程造成的内存泄漏
产生原因:
由于线程是Activity的内部类,所以在Thread中保存了Activity的一个引用,当Thread的run函数没有结束时,Thread是不会被销毁的,因此引用的Activity也不会被销毁,造成内存泄漏,AsyncTask内部也是Handler机制,也存在内存泄漏的风险
解决方案:
将线程的内部类,改为静态内部类
在线程内部采用弱引用保存Context引用
//线程造成的内存泄漏
public class ThreadLeakActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
testThreadLeak();
}
private void testThreadLeak() {
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
}
}).start();
new AsyncTask(){
@Override
protected Object doInBackground(Object[] objects) {
return null;
}
}.execute();
}
//解决方案:改成静态内部类
static class MyAsyncTask extends AsyncTask {
private WeakReference<Context> weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Object doInBackground(Object[] objects) {
SystemClock.sleep(2000);
return null;
}
}
}
WebView造成的内存泄漏
解决方案:
避免在xml直接写webview控件,可以使用一个占位布局如:LinearLayout,然后在代码中动态创建一个webView,
linearLayout.addView(new MyWebview(getApplicationContext()));
在当前Activity销毁的时候 在onDestroy方法中销毁这个WebView,释放webview内存
将包含WebView的Activity放在一个单独的进程中,不需要时将进程销毁,从而释放所有所占内存
public class WebLeakActivity extends AppCompatActivity {
private WebView mWebView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = findViewById(R.id.webview);
mWebView.loadUrl("https://www.baidu.com");
}
@Override
protected void onDestroy() {
destoryWebView();
//webview在一个独立进程中创建,不使用的时候直接kill掉进程
Process.killProcess(Process.myPid());
super.onDestroy();
}
private void destoryWebView() {
if (mWebView != null) {
mWebView.pauseTimers();
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
}
}
资源对象未关闭造成的内存泄漏
产生原因:
资源性对象使用了缓冲区,缓冲区不仅在JVM内,JVM之外也有。如果仅仅把变量设置为null,而不关闭它们,缓冲区得不到释放,往往造成内存泄露。
常见场景:如Cursor、File等一些Closeable对象
解决方案:
一般在finally中关闭资源型对象,而后设置对象为null
注册对象未注销造成的内存泄漏
产生原因:
观察者模式中,如果注册对象不再使用时,未及时注销,会导致观察者列表中维持这对象的引用,阻止垃圾回收,导致内存泄露。
常见场景:动态注册BroadcastReceiver,注册PhoneStateListener,注册EventBus,还有自定义使用观察者模式 等等
解决方案:
在onDestroy()中进行解注册
容器中对象未及时清理导致内存泄露
产生原因:
容器类一般拥有较长的生命周期,若内部不再使用的对象不及时清理,内部对象边一直被容器类引用
常见的容器类:线程池、对象池、图片缓存池等
解决方案:
注意对对象置空释放
静态View导致内存泄露
产生原因:
当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦View attach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View有事一个静态变量,所以导致Activity不被回收
解决方案:
在使用静态View时,需要确保在资源回收时,将静态View detach掉。
属性动画未及时关闭导致内存泄露
产生原因:
在使用ValueAnimator或者ObjectAnimator时,如果没有及时做cancel取消动画,就可能造成内存泄露。 因为在cancel方法里,最后调用了endAnimation(); ,在endAnimation里,有个AnimationHandler的单例,会持有属性动画对象的引用
解决方案:
在onDestory时,调用动画的cancel方法
其他常见的引起内存泄漏原因
构造Adapter时,没有使用缓存的 contentView
Bitmap在不使用的时候没有使用recycle()释放内存
避免代码设计模式的错误造成内存泄露;譬如循环引用,A持有B,B持有C,C持有A,这样的设计谁都得不到释放
警惕线程未终止造成的内存泄露,如在Activity中关联了一个生命周期超过Activity的Thread,在退出Activity时切记结束线程。HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了Activity生命周期,我们必须手动在Activity的销毁方法中调用thread.getLooper().quit();才不会泄露