内存泄漏:
静态对象内存泄漏
static Object cc=null
- 在
Dalvik
虚拟机中,static变量
所指向的内存引用
,如果不把它设置为null
,那么JVM
虚拟机就会在内存中一直保留这个对象
,这个对象
不会被垃圾回收器清理,GC
是永远不会回收这个对象
的,直到应用退出。
非静态内部类内存泄漏
- 1.编译器
自动
为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象的引用
- 2.编译器自动为内部类的
构造方法添加
一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为1
中添加的成员变量赋值; - 3.在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。
越来越多的内存得不到回收的时候,最终就有可能导致内存溢出,下面说一下使用staitc
属性所导致的内存泄漏的问题。
常用的Utils中的static修饰
public class ToastUtil {
private static Toast toast;
public static void show(Context context, String message) {
if (toast == null) {
toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
} else {
toast.setText(message);
}
toast.show();
}
}
分析:
static
修饰的toast
对象,在show
方法中这个对象同时对context
持有了引用。toast
是static
修饰的,意味着toast
将不会从内存中消失,那么其持有的引用对象context
将一直保持强引用,也不会在内存中消失。如果传个占用大内存的Activity
的context
进来,那么将会导致大量的内存泄漏。
解决方案:
- 将
contex
t改为context.getApplicationContext()
,由于ApplicationContext
是一个全局的context
,会在app运行期间一直存在,就不存在内存泄露的问题了 - 创建
Application
类,提供获取ApplicationContext
的方法,直接在show方法中获取,不需要每次都传context
了(推荐) 代码如下:
//Application类
public class App extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext(){
return context;
}
}
//
//Toast工具类
public class ToastUtil {
private static Toast toast;
public static void show(String message) {
if (toast == null) {
toast = Toast.makeText(App .getContext(), message, Toast.LENGTH_SHORT);
} else {
toast.setText(message);
}
toast.show();
}
}
Handler造成的内存泄漏
public class MainActivity extends AppCompatActivity
{
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...更新UI操作
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
....
....
initDatas();
}
private void initDatas()
{
//...子线程获取数据,在主线程中更新UI
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
分析:
这种创建Handler
的方式会造成内存泄漏,由于mHandler
是Handler
的非静态匿名内部类
的实例
,持有外部类Activity
的引用
。消息队列是在一个Looper
线程中不断轮询
处理消息,如果当前Activity
退出时消息队列中还有未处理
的消息或者正在处理
消息,而消息队列中的Message
持有mHandler
实例的引用,而mHandler
又持有Activity
的引用
,导致Activity
的内存资源无法回收,引发内存泄漏。而别的匿名内部类比如new OnClickListener()
因为得到及时回收并非一直被触发,所以会被回收。
解决方案:
public class MainActivity extends AppCompatActivity
{
private MyHandler mHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null)
{
//...更新UI操作
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
...
...
initDatas();
}
private void initDatas()
{
//...子线程获取数据,在主线程中更新UI
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
//移除消息队列中所有消息和所有的Runnable
mHandler.removeCallbacksAndMessages(null);
}
}
静态内部类:
1.静态内部类是不需要依赖于外部类的
2.它不能使用外部类的非static成员变量或者方法(在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象)
非静态内部类:
1.成员内部类可以无条件访问外部类的所有成员属性和成员方法,包括private成员和静态成员。
2.当成员内部类拥有和外部类同名的成员变量或者方法时,外部类中的属性和方法会发生隐藏现象,即默认情况下访问的是成员内部类的成员,可以通过下面这种形式对外部类的属性和方法进行访问,外部类.this.成员变量,外部类.this.成员方法。
3.外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问。
4.如果要创建成员内部类的对象,前提是必须存在一个外部类的对象
局部内部类:
1.局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
https://blog.csdn.net/alionsss/article/details/77141738
https://blog.csdn.net/ys408973279/article/details/50389200?utm_source=blogxgwz0