本文只针对java中的弱引用进行一些分析,如有出入还请多指正。
在分析弱引用之前,先阐述一个概念:什么是对象可到达和对象不可到达状态。
其实很简单,我举个例子:
现在有如下两个类class A class B,在JVM上生成他们两个类的实例分别为 instance a instance b
有如下表达式:
A a = new A();
B b = new B();
两个强引用对象就生成了,好吧,那么这个时候我做一下修改:
A a = new A();
B b = new B(a);
B的默认构造函数上是需要一个A的实例作为参数的,那么这个时候 A和B就产生了依赖,也可以说a和b产生了依赖,我们再用一个接近内存结构的图来表达:
a是对象A的引用,b是对象B的引用,对象B同时还依赖对象A,那么这个时候我们认为从对象B是可以到达对象A的。
于是我又修改了一下代码
A a = new A();
B b = new B(a);
a = null;
A对象的引用a置空了,a不再指向对象A的地址,我们都知道当一个对象不再被其他对象引用的时候,是会被GC回收的,很显然及时a=null,那么A对象也是不可能被回收的,因为B依然依赖与A,在这个时候,造成了内存泄漏!
那么如何避免上面的例子中内存泄漏呢?
很简单:
A a = new A();
B b = new B(a);
a = null;
b = null;
这个时候B对象再也没有被任何引用,A对象只被B对象引用,尽管这样,GC也是可以同时回收他们俩的,因为他们处于不可到达区域。
弱引用来了!
A a = new A();
WeakReference wr = new WeakReference(a);
//B b = new B(a);
当 a=null ,这个时候A只被弱引用依赖,那么GC会立刻回收A这个对象,这就是弱引用的好处!他可以在你对对象结构和拓扑不是很清晰的情况下,帮助你合理的释放对象,造成不必要的内存泄漏!!
AbstractRefreshableApplicationContext.java closeBeanFactory注意在置空键和值之后,spring还调用了serializableFactories .clear().来优化内存
- @Override
- protected final void closeBeanFactory() {
- synchronized (this.beanFactoryMonitor) {
- this.beanFactory.setSerializationId(null);
- this.beanFactory = null;
- }
- }
- /** Map from serialized id to factory instance */
- private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
- new ConcurrentHashMap<String, Reference<DefaultListableBeanFactory>>(8);
- /**
- * Specify an id for serialization purposes, allowing this BeanFactory to be
- * deserialized from this id back into the BeanFactory object, if needed.
- */
- public void setSerializationId(String serializationId) {
- if (serializationId != null) {
- serializableFactories.put(serializationId, new WeakReference<DefaultListableBeanFactory>(this));
- }
- else if (this.serializationId != null) {
- serializableFactories.remove(this.serializationId);
- }
- this.serializationId = serializationId;
- }
自己演示
- import java.lang.ref.WeakReference;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- public class TestDemo {
- public static void main(String[] args) {
- Map<Object, Object> map = new HashMap<>();
- Student a = new Student();
- Student b = new Student();
- Student c = new Student();
- Student d= new Student();
- WeakReference wr1 = new WeakReference(a);
- WeakReference wr2 = new WeakReference(b);
- WeakReference wr3 = new WeakReference(c);
- WeakReference wr4 = new WeakReference(d);
- map.put(wr1, wr3);
- map.put(wr2, wr4);
- b=null;
- System.gc();
- WeakReference temp1= (WeakReference) map.get(wr2);
- Student dd=(Student) temp1.get();
- dd.run("222");
- c=null;
- System.gc();
- WeakReference temp= (WeakReference) map.get(wr1);
- Student aa=(Student) temp.get();
- aa.run("1111");
- }
- }
- class Student{
- public void run(String str) {
- System.out.println("its run "+str);
- }
- }
- its run 222
- Exception in thread "main" java.lang.NullPointerException
- at test.TestDemo.main(TestDemo.java:31)
总结:发现当将将map的键设为弱引用时置空,不会出现空指针异常,说明没有被垃圾回收,当将map的value设为弱引用置空,垃圾回收后会出现空指针异常,说明没有存在内存泄漏。即当弱引用被强引用的时候不会被垃圾回收。
- public static void main(String[] args) {
- Map<Object, Object> map = new HashMap<>();
- Student a = new Student();
- Student b = new Student();
- Student c = new Student();
- Student d= new Student();
- map.put(a, c);
- map.put(b, d);
- a=null;
- System.gc();
- for (Entry<Object, Object> str : map.entrySet()) {
- Student student=(Student) str.getValue();
- student.run("11");
- }
- d=null;
- System.gc();
- Student object1 = (Student) map.get(b);
- object1.run("222");
- Student object = (Student) map.get(a);
- object.run("111");
- }
- its run 11
- its run 11
- its run 222
- Exception in thread "main" java.lang.NullPointerException
- at test.TestDemo.main(TestDemo.java:32)
通过上述方法,发现当map键为普通引用的时候,当把键置空,则发现无法通过键获取value,但是实际上该值还存在,出现了内存泄漏,当将map的value设为普通引用,发现在在value置空后,并没有进行垃圾回收,出现了内存泄漏