要想更好的理解SoftReferrence和WeakReference的机制,首先要说下Java中的对象和对象引用的概念。
对象,就是类的实例;对象引用,可以理解为操作对象的标识符,类似于c语音中的指针。
举个例子
Map map = new HashMap(); 复制代码
map即是对象引用;真正的对象是通过new创建的。
下面结合这个图说下对象和对象引用的相关问题
Obj1和Obj2就是通过new创建的内存,在Java中使用的是堆内存。
o1和o2就是对象引用,在Java中方法内的对象引用使用的是栈内存。在方法执行完成,就会释放。
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中的引用类型
Object obj = new Object(); 复制代码
如果不将对象引用置null,那么GC是绝对不会回收内存,即使发生OOM。
Object obj = new Object(); ... obj = null;//这时候为垃圾回收器回收这个对象,至于什么时候回收,取决于垃圾回收器的算法 复制代码
String sf = new String(“SoftReference”); SoftReference sfRefer = new SoftReference(sf); sf = null; 复制代码
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 复制代码