此篇主要记录了两点:
1、内存泄漏与内存溢出的区别
2、引起内存泄漏的几种情况
3、和内存相关的一些知识点记录,这部分另写笔记。
一、技术点
1、内存泄漏与内存溢出的区别
- 内存泄漏:指程序在申请内存后,被某个对象一直持有,导致无法释放已申请的内存空间。一次内存泄漏的危害可以忽略,但内存泄漏堆积后果很严重,无论多少内存,迟早会被占光。
- 内存溢出:指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory。Android系统为每个应用程序申请到的内存有限,一般为64M或者128M等,我们可以在清单文件中进行配置,android:largeheap = "true" 从而给APP申请更大的内存空间。
虚拟机:JVM的作用是把平台无关的.class里面的字节码翻译成平台相关的机器码,来实现跨平台。Dalvik和Art时安卓中使用的虚拟机。
注:本地方法栈,属于native层,暂不需要管,它和Java层是不一样的垃圾回收机制。
内存溢出会发生在堆内存和虚拟机栈:
-
堆内存溢出
例1:
例2:如生产者与消费者模型,如注册回调,忘记注销。添加到队列,忘记控制队列大小。
例3:fastjson解析(间接)循环引用。(这里例子还不是很理解,没有遇到过) 栈(虚拟栈)内存溢出
方法递归。
内存泄漏会发生在方法区、堆内存和虚拟机栈。
具体情况见Q3。
2、垃圾回收机制的原理是什么
3、详细说说什么情况下会出现Android内存泄漏(六大类)
(1)单例使用不当(生命周期不一样)
说明:单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄漏。
public class AppSetting {
private static AppSetting mInstance;
private Context mContext;
private AppSetting(Context context) {
this.mContext = context;
}
public static AppSetting getInstance(Context context) {
if (mInstance == null) {
mInstance = new AppSetting(context);
}
return mInstance;
}
}
//使用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppSetting.getInstance(this);
}
}
解决:
将AppSetting修改如下:
public class AppSetting {
private static AppSetting mInstance;
private Context mContext;
private AppSetting(Context context) {
this.mContext = context.getApplicationContext();
}
public static AppSetting getInstance(Context context) {
if (mInstance == null) {
mInstance = new AppSetting(context);
}
return mInstance;
}
}
(2)静态变量导致内存泄漏
静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放。
public class MainActivity extends AppCompatActivity {
private static Info sInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sInfo = new Info(this);
}
}
class Info {
Activity mActivity;
public Info(Activity activity) {
mActivity = activity;
}
}
分析:
Info作为Activity的静态成员,并且持有Activity的引用,但是sInfo作为静态变量,生命周期肯定比Activity长。所有当Activity退出后,sInfo仍然引用了Activity,导致Activity不能被回收,引起内存泄漏。
解决:
在Activity退出时,可以在onDestory中,把静态引用变量置为null。
@Override
protected void onDestroy() {
super.onDestroy();
if (sInfo != null) {
sInfo = null;
}
}
(3)非静态内部类导致内存泄漏
非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄漏。常见于Handler、Thread、AsyncTask。
例1:Handler
public class MainActivity1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start() {
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
}
}
};
}
分析:
在此例中,mHandler会默认持有外部类(MainActivity1)的引用,导致MainActivity1不能被回收,引起内存泄漏。
解决:
通过弱引用解决。
public class MainActivity1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start() {
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}
private Handler mHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private WeakReference mActivity1WeakReference;
public MyHandler(MainActivity1 activity1) {
mActivity1WeakReference = new WeakReference<>(activity1);
}
@Override
public void handleMessage(@NonNull Message msg) {
MainActivity1 activity1 = mActivity1WeakReference.get();
switch (msg.what) {
//处理逻辑
}
}
}
}
例2:Thread
public class MainActivity1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//处理任务
SystemClock.sleep(1000L);
}
}
}).start();
}
}
分析:
1、内部匿名的Thread实例会长久运行,不会被系统GC回收。
2、非静态内部类会持有外部类的引用。
在此例中,即使MainActivity1退出了,但是始终有一个thread持有它的引用,导致MainActivity1不能被回收,引起内存泄漏。
解决:
1、加上static后,内部类就不会持有MainActivity1的隐式引用了。
public class MainActivity1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread().start();
}
private static class MyThread extends Thread {
@Override
public void run() {
while (true) {
//处理任务
SystemClock.sleep(1000L);
}
}
}
}
2、解决Thread无法回收问题
public class MainActivity1 extends AppCompatActivity {
private MyThread mMyThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyThread = new MyThread();
mMyThread.start();
}
private static class MyThread extends Thread {
private boolean mIsRunning = false;
@Override
public void run() {
mIsRunning = true;
while (mIsRunning) {
//处理任务
SystemClock.sleep(1000L);
}
}
public void close() {
mIsRunning = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mMyThread.close();
}
}
分析:
给Thread添加结束的标志位。当thread任务执行完成后,Java会帮我们把回收线程。因此,我们要养成为thread设置退出逻辑的习惯,保证thread可以运行结束。
注:
Java threads会一直存在,只有当线程运行完成或被杀死掉,线程才会被回收。
(4)未取消注册或回调导致内存泄漏
我们在Activity中注册广播后,如果在Activity销毁后不取消注册,那么这个广播会一直存在,同上面所说的非静态内部类一样持有Activity引用,导致内存泄漏。因此注册广播后一定要在Activity销毁后取消注册。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.registerReceiver(mReceiver, new IntentFilter());
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//收到广播时的处理
}
};
@Override
protected void onDestroy() {
super.onDestroy();
this.unregisterReceiver(mReceiver);
}
}
(5)资源未关闭或释放导致内存泄漏
在使用流等资源时要及时关闭,这些资源在进行读写时通常都使用了缓冲,如果不及时关闭,这些缓存对象就会一直被占用,引起内存泄漏。
4、什么是内存抖动,产生的本质是什么?(年轻堆与老年堆机制)
5、你是怎么处理crash异常的,对于不能定位行数的问题怎么解决
6、你是怎么对内存进行管理的?(Bitmap,对象)
二、相关知识
1、内存详解
2、垃圾回收机制
3、GcRoot原理详解
参考:
1、面试官:内存泄漏连环问。被问懵了?来看看这部视频_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
2、内存泄露:Thread是如何造成内存泄露的 - (jianshu.com)