要想更好的理解SoftReferrence和WeakReference的机制,首先要说下Java中的对象和对象引用的概念。
对象,就是类的实例;对象引用,可以理解为操作对象的标识符,类似于c语音中的指针。
举个例子
Map map = new HashMap();
map即是对象引用;真正的对象是通过new创建的。
下面结合这个图说下对象和对象引用的相关问题
Obj1和Obj2就是通过new创建的内存,在Java中使用的是堆内存。
o1和o2就是对象引用,在Java中方法内的对象引用使用的是栈内存。在方法执行完成,就会释放。
具体堆和栈内存,可以参考。https://my.oschina.net/android520/blog/744490
Java中的GC主要针对的就是Obj1和Obj2这种堆内存。GC原理简单描述就是,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。
Java中的内存泄漏,主要指,在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。
再举个极端的例子
Vector v = new Vector(10);
for (int i = 1; i < 100; i++){
Object o = new Object();
v.add(o);
o = null ;
}
在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。
还有比如Android中的Activity内存泄漏,下面的情况,当Activity关闭后,也不会释放Activity占用的内存,因为静态变量activity还在引用Activity。
static Activity activity;
public void setActivity(Activity a) {
this.activity = a;
}
下面介绍下Java中的引用类型
1. 强引用(默认存在)
Object obj = new Object();
如果不将对象引用置null,那么GC是绝对不会回收内存,即使发生OOM。
Object obj = new Object();
...
obj = null;//这时候为垃圾回收器回收这个对象,至于什么时候回收,取决于垃圾回收器的算法
2. 软引用(SoftReference)
在内存空间足够的情况下,除非内存空间接近临界值、jvm即将抛出oom的时候,垃圾回收器才会将该引用对象进行回收,避免了系统内存溢出的情况。
String sf = new String(“SoftReference”);
SoftReference sfRefer = new SoftReference(sf);
sf = null;
3. 弱引用(WeakReference)
当垃圾回收器扫描到弱引用的对象的时候,不管内存空间是否足够,都会直接被垃圾回收器回收。不过也不用特别担心,垃圾回收器是一个优先级比较低的现场,因此不一定很快可以发现弱引用的对象。
String wf = new String(“WeakReference”);
WeakReference sfRefer = new WeakReference(wf);
wf = null;
下面通过例子说明下,SoftReference释放时机
public class JavaTest {
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];//用来占用大量内存
byte[] soft = new byte[900000000];
SoftReference softReference = new SoftReference(soft);
soft = null;
// byte[] strong2 = new byte[900000000];
System.out.println("SoftReference : " + softReference.get());
}
}
// 下面是输出结果
// SoftReference : [B@1b6d3586
// 该例子中,注释掉strong2的内存申请,则内存空间没有达到上限,因此没有释放soft引用指向的内存。
public class JavaTest {
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];//用来占用大量内存
byte[] soft = new byte[900000000];
SoftReference softReference = new SoftReference(soft);
soft = null;
byte[] strong2 = new byte[900000000];//用来再次申请大量内存,是当前内存不够用
System.out.println("SoftReference : " + softReference.get());
}
}
// 下面是输出结果
// SoftReference : null
// 该例子中,打开strong2的内存申请,则内存空间达到上限,因此释放soft引用指向的内存。
public class JavaTest {
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];//用来占用大量内存
byte[] soft = new byte[900000000];
SoftReference softReference = new SoftReference(soft);
// soft = null;//该行代码,将强引用置null,只保留软引用,才能保证内存不够时释放该内存,否则发生OOM
byte[] strong2 = new byte[900000000];//用来再次申请大量内存,是当前内存不够用
System.out.println("SoftReference : " + softReference.get());
}
}
// 下面是输出结果
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
// Java代码创建SoftReference后,必须将soft原对象设置null
下面看下WeakReference的释放时机
public class JavaTest {
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];//用来占用大量内存
byte[] weak = new byte[900000000];
WeakReference weakReference = new WeakReference<>(weak);
weak = null;//该行代码,将强引用置null,只保留弱引用,才能保证释放该内存,否则发生OOM
// System.gc();//用来强制进行gc
// byte[] strong2 = new byte[900000000];
System.out.println("WeakReference : " + weakReference.get());
}
}
// 下面是输出结果
// WeakReference : [B@1b6d3586
// 在正常不执行gc情况,WeakReference引用指向的内存不会被释放
public class JavaTest {
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];//用来占用大量内存
byte[] weak = new byte[900000000];
WeakReference weakReference = new WeakReference<>(weak);
weak = null;//该行代码,将强引用置null,只保留弱引用,才能保证释放该内存,否则发生OOM
System.gc();//用来强制进行gc
// byte[] strong2 = new byte[900000000];
System.out.println("WeakReference : " + weakReference.get());
}
}
// 下面是输出结果
// WeakReference : null
// 执行gc情况,即使内存没达到上限,WeakReference引用指向的内存会被释放
public class JavaTest {
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];//用来占用大量内存
byte[] weak = new byte[900000000];
WeakReference weakReference = new WeakReference<>(weak);
weak = null;//该行代码,将强引用置null,只保留弱引用,才能保证释放该内存,否则发生OOM
// System.gc();//用来强制进行gc
byte[] strong2 = new byte[900000000];
System.out.println("WeakReference : " + weakReference.get());
}
}
// 下面是输出结果
// WeakReference : null
// 可以看出,申请内存达到上限时,有可能执行gc,WeakReference引用指向的内存会被释放
下面再来看下方法内的引用对象的情况
public class JavaTest {
private static Map<String, WeakReference> weakMap = new HashMap<>();
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];
byte[] weak = new byte[900000000];
WeakReference weakReference = new WeakReference<>(weak);
// weak = null;
weakMap.put("weak", weakReference);
// System.gc();
byte[] strong2 = new byte[900000000];
System.out.println("WeakReference : " + weakMap.get("weak").get());
}
}
// 下面是输出结果
// Exception in thread "main" java.lang.OutOfMemoryError:
下面我们将创建weakReference的方法放到另外的方法里再看看运行效果
public class JavaTest {
private static Map<String, WeakReference> weakMap = new HashMap<>();
public static void main(String[] args) {
byte[] strong1 = new byte[100000000];
newWeak();
// System.gc();
byte[] strong2 = new byte[900000000];
System.out.println("WeakReference : " + weakMap.get("weak").get());
}
private static void newWeak() {
byte[] weak = new byte[900000000];
WeakReference weakReference = new WeakReference<>(weak);
// weak = null;
weakMap.put("weak", weakReference);
}
}
// 下面是输出结果
// WeakReference : null
// 可以看到在newWeak中,没有将变量weak设置成null,在内存不够时,也会释放WeakReference中的内存
// 所以可以看出,在newWeak方法结束后,weak这个引用,自动设置成了null