内存溢出与内存泄漏的区别:
内存溢出是由于应用所消耗的内存或者应用申请的内存超出了虚拟机分配的内存,也就是内存不够用了。
内存泄漏是某个不再使用对象由于被其他实例引用,导致不能被GC回收,而导致的内存不能释放。
Java 内存分配策略
Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。
静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
栈区 :当方法被执行时,方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。
栈与堆的区别:
在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。
堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。
一般的单例模式Android代码:
Java
private static CommUtils instance;
private Context context;
public CommUtils(Context context) {
this.context = context;
}
public static CommUtils getInstance(Context context){
if (instance == null){
instance = new CommUtils(context);
}
return instance;
1
2
3
4
5
6
7
8
9
10
privatestaticCommUtilsinstance;
privateContextcontext;
publicCommUtils(Contextcontext){
this.context=context;
}
publicstaticCommUtilsgetInstance(Contextcontext){
if(instance==null){
instance=newCommUtils(context);
}
returninstance;
该类在MainActivity中的使用如下代码:
Java
public class PerformsActivity{
@override
protected void onCreate(Bundle saveInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_performs);
CommUtils commUtils = CommUtils.getInstance(this);
}
}
1
2
3
4
5
6
7
8
publicclassPerformsActivity{
@override
protectedvoidonCreate(BundlesaveInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_performs);
CommUtilscommUtils=CommUtils.getInstance(this);
}
}
上面的这段代码是我们在写单例的时候经常写的,但是在这个单例的使用是存在内存泄露风险的;
原因是:当手机进行横竖屏切换的时候或者Activity过多内存使用紧张的时候,系统销毁Activity,但在这个时候该Activity被CommUtils工具类所持有,导致Activity无法被系统回收从而导致内存泄露
有两种方式解决该单例所导致的内存泄露:
第一种方式是使用全局的Application上下文Context参数使用CommUtils;
第二种方式使用弱引用的方式解决代码如下。
第一种:
Java
public class PerformsActivity{
@override
protected void onCreate(Bundle saveInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_performs);
CommUtils commUtils = CommUtils.getInstance(getApplicationContext());
}
}
1
2
3
4
5
6
7
8
publicclassPerformsActivity{
@override
protectedvoidonCreate(BundlesaveInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_performs);
CommUtilscommUtils=CommUtils.getInstance(getApplicationContext());
}
}
第二种:
Java
public class CommUtils {
private static WeakReference WeakReferenceInstance;
private Context context;
public CommUtils(Context context) {
this.context = context;
}
public static CommUtils getInstance(Context context){
if (WeakReferenceInstance == null || WeakReferenceInstance.get() == null) {
WeakReferenceInstance = new WeakReference(new CommUtils(context));
}
return WeakReferenceInstance.get();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
publicclassCommUtils{
privatestaticWeakReferenceWeakReferenceInstance;
privateContextcontext;
publicCommUtils(Contextcontext){
this.context=context;
}
publicstaticCommUtilsgetInstance(Contextcontext){
if(WeakReferenceInstance==null||WeakReferenceInstance.get()==null){
WeakReferenceInstance=newWeakReference(newCommUtils(context));
}
returnWeakReferenceInstance.get();
}
}
建议:
1,尽量不要用一个生命周期长于Activity的对象来持有Activity的引用。
2,在需要传入Context的时候尽量考虑使用Application的Context,而不是Activity的。
3,在Activity中尽量避免使用生命周期不受控制的非静态类型的内部类,可以使用静态类型的内部类加上弱引用的方式实现。