一款音乐播放器,基于5.0新特性,效果炫酷,点击看源码
Java语言是垃圾回收语言的一种,好处就是开发者不用特意的管理内存的分配,但是java仍然存在很多内存泄漏的可能,不好好处理内存泄漏的问题,最终会导致app的奔溃。
内存泄漏与内存溢出的区别
内存泄漏:程序向系统申请分配内存空间后(new 创建对象申请内存空间),在使用完毕后,申请的内存空间没有释放掉,其他的程序也无法使用这部分内存空间,直到程序结束。
内存溢出:程序向系统申请的内存空间超出了系统能给的,就会内存溢出(OOM)。
大量的内存泄漏会导致内存溢出(OOM);
垃圾回收机制
大家都知道java中的栈存放基本数据类型变量和对象的引用,堆存放引用的对象。
Java垃圾回收可以自动清空堆中不在使用的对象。换句话说就是可以自动清理没有引用指向的对象。比如被置空的对象。
为了可以更加灵活的控制对象的生命周期,JDK1.2将对象的引用分为四个级别:强引用,软引用,弱引用,虚引用.
1.强引用
强引用是最常见的一种引用类型。比如:Student stu = new Student();强引用自己存储在栈内存中,存储的地址指向堆内存中的对象。当堆内存中的对象没有任何强引用指向时,或者 student = null时,系统进行垃圾回收时,这部分堆内存就会被回收掉。
2.软引用
软引用一般使用形式:
Student stu = new Student();
SoftReference<Student> soft = new SoftReference<Student>(stu);
持有软引用的对象进行垃圾回收需要的条件:
1.没有强引用对象指向它
2.当虚拟机内存不足时
所以软引用指向的对象占据堆内存的时间延长了,直到虚拟机内存不足时,才会被回收掉。
3.弱引用
弱引用一般使用形式:
Student stu = new Student();
WeakReference<Student> weak = new WeakReference<Student>(stu);
弱引用不改变原有强引用对象的回收时机,一旦其指向对象没有任何强引用对象时,就可以被回收掉。
4.虚引用
虚引用PhantomReference必须结合ReferenceQueue使用,同样不会改变其指向对象的回收时机。有两个特点:
1.PhantomReference只有一个构造函数
PhantomReference(T referent, ReferenceQueue<? super T> q)
2.不管其指向对象有没有强引用指向,get()的返回结果都是null。
android常见的内存泄漏
内存泄漏中最严重的当属Activity对象,一般来说一个Activity占用着很多资源,也就是很多内存空间,如果不在使用时,不能进行回收的话就有严重的内存泄漏了。Activity内存泄漏的核心就是在生命周期外,仍然持有此Activity的引用。下面讲一下常见的内存泄漏的栗子和解决方法:
static activity
private static MainActivity activity;
void setStaticActivity() {
activity = this;
}
static变量是贯穿整个应用的生命周期的,被泄漏的activity会一直存在于应用的进程中,不会被垃圾回收器回收。
解决:这种写法是有理由来使用的,为了避免内存泄漏,我们需要正确释放引用,让垃圾回收器在它销毁的时候对它进行回收。我们可以控制引用的“强度”,Activity对象泄漏是由于需要销毁时,仍然有强引用的指向,只要有强引用存在就无法被回收。弱引用不会阻止对象的内存释放,
private static WeakReference<MainActivity> activityReference;
void setStaticActivity() {
activityReference = new WeakReference<MainActivity>(this);
}
static view
private static View view;
void setStaticView() {
view = findViewById(R.id.sv_button);
}
由于view持有其宿主Activity的引用,导致的内存泄漏问题也是一样的严重,
解决:对于这种泄漏,弱引用是一种有效的解决方式,同时也看可以在Activity生命周期结束时清除引用,
private static View view;
@Override
public void onDestroy() {
super.onDestroy();
if (view != null) {
view = null;
}
}
持有内部类的成员变量
private static Object inner;
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
}
非静态内部类会持有外部类的引用,inner持有外部类的引用,又被static修饰,这样的成员变量非常容易导致内存泄漏,static贯穿整个应用的生命周期,假如外部类生命周期结束了,但是inner被static修饰,与应用的生命周期一致,这部分内存在应用结束前就不能被释放再利用了,造成内存泄漏。
解决:下面持有内部类的成员变量是可以的
private Object inner;
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
}
匿名内部类
AsyncTask
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
}
Handler
void createHandler() {
new Handler() {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}.postDelayed(new Runnable() {
@Override public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
Thread
void scheduleTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
以上的匿名内部类都有可能导致内存泄漏。这些类会产生后台线程,Java中的线程是全局的,也就是这些线程会持有匿名内部类的引用,而匿名内部类又持有外部类的引用,线程如果长时间运行的话,一直持有Activity的引用,当Activity销毁时,仍然持有Activity的引用,直到线程结束,这样就导致了内存泄漏。
解决:静态内部类不会持有外部类的引用,使用静态内部类打破链式引用
AsyncTask
private static class NimbleTask extends AsyncTask<Void, Void, Void> {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}
void startAsyncTask() {
new NimbleTask().execute();
}
Handler
private static class NimbleHandler extends Handler {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}
private static class NimbleRunnable implements Runnable {
@Override public void run() {
while(true);
}
}
void createHandler() {
new NimbleHandler().postDelayed(new NimbleRunnable(), Long.MAX_VALUE >> 1);
}
Thread
private static class NimbleTimerTask extends TimerTask {
@Override public void run() {
while(true);
}
}
void scheduleTimer() {
new Timer().schedule(new NimbleTimerTask(), Long.MAX_VALUE >> 1);
}
Android系统服务使用不当
void registerListener() {
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
我们在使用Android系统服务时,会将Activity作为监听器,这样的话引用链在传递事件和回调中形成了,只要Activity维持注册监听状态,引用就会一直存在,内存就不会被释放了。
解决:在Activity生命周期结束时注销监听器
private SensorManager sensorManager;
private Sensor sensor;
@Override
public void onDestroy() {
super.onDestroy();
if (sensor != null) {
unregisterListener();
}
}
void unregisterListener() {
sensorManager.unregisterListener(this, sensor);
}