Java 中的强引用 、软引用、弱引用、虚引用

这里整理一下之前一直理解得不是很清楚的 Java 的引用;

Java 开发不像 C 语言有指针,不能通过编码回收内存,完全靠垃圾回收器不定时来进行垃圾回收;

虽然垃圾回收器的工作是靠 JVM 来自动控制,但是做为 Java 程序员仍然可以通过编程在一定程序上与垃圾回收器进行交互,以帮助程序员稍微精细的控制内存回收,帮助垃圾回收器更好的管理内存的回收工作;

Java 中有存在着四种引用类型:强引用 、软引用、弱引用、虚引用;四种类型的引用强度由强至弱,依次递减。

强引用

强引用是 Java 中最常见的也是最直接的引用方式,例如

Person p = new Person("Tom");

这样的代码就会生成一个 Person对象,和一个指向此对象的强引用;通常一个对象可能有多个强引用,只要对象存在强引用,垃圾回收器就不会回收这块内存。堆中的存在强引用的对象越来越多,最终内存不够用时,JVM就会抛出 java.lang.OutOfMemoryError这个错误!

软引用

软引用相对强引用,引用强度略低,通过java.lang.ref.SoftReference来实现,与强引用的区别就是:垃圾回收器在 JVM 内存不够用的时候才回收回软引用所引用的对象内存;如果已经释放掉所有的软引用所引用的对象的内存,依旧内存不够时才会抛出java.lang.OutOfMemoryError错误;因为软引用的这个特性,它很适合拿来实现程序中缓存数据,比喻程序启动时需要从数据库中读取大量没有太高实时性要求的配置数据时,就可以使用软引用来缓存这些数据,不必每次访问时去查数据库,只有当内存不够用时缓存的这块数据空间被回收,才无法命中去查数据库,这样减少了数据库操作;

示例代码:

public class DBConfigCache {
    private SoftReference<Config> configCache;
    
    public DBConfigCache() {
    }
    
    private Config readConfigFromDB() {
         return new Confit();//忽略从数据库查的代码
    }
    public Config getConfig() {
        Config config = configCache == null ? null : configCache.get();//获取得配置对象的强引用;
        if(config == null) {
             //第一次读时为空
             //缓存的数据被回收时为空,
             //从数据库读
             config = readConfigFromDB();
             configCache = new SoftReference<>(config);
        }
        return config;
    }
}

弱引用

弱引用,相对于软引用,引用强度弱低,通过java.lang.ref.WeakReference来实现,弱引用所引用的对象,不保证不被垃圾回收器回收。只有弱引用的对象,不存在其他引用类型,会被垃圾回收器回收;弱引用应用场景很少,通常用来解决对象间存活有耦合关系的引用问题。最常见就是使用在集合中,比喻当一个对象被当做键值往哈希表中来存放一个值,希望此对象被垃圾回收器回收后也将在哈希表中对应存放的值也删除掉,那么就要可以使用弱引用实现的java.util.WeakHashMap;当只哈希表中的键对象不存其他引用类型时,被垃圾回收器回收后,同时也会删除键对应的值;

可以通过如下代码测试:

private Map<Person, Object> map = new WeakHashMap<Person, Object>();

    public void testWeakHashMap () {

        Person p1 = new Person("test2");
        Person p2 = new Person("test2");
        map.put(p1, new Object());
        map.put(p2, new Object());

        System.out.println("哈希表大小:" + map.size());

        System.gc();
        try {
            Thread.sleep(3000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("哈希表大小:" + map.size());


        p1 = null;
        System.gc();
        try {
            Thread.sleep(3000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("哈希表大小:" + map.size());

    }

方法执行结果:

哈希表大小:2
哈希表大小:2
哈希表大小:1

虚引用

虚引用,使用java.lang.ref.PhantomReference来实现;虚引用跟字面意义一样,如果一个对象存在虚引用,则形同虚设,与没有引用一样;实现的时候需要得传一个引用队列java.lang.ref.ReferenceQueue;我们知道内存回收时因为回收算法(比喻『标记-清除-整理』)的原因是在多个执行周期内进行的;在对象被垃圾回收器识别为要回收时,会将此虚引用加到引用队列中,引用队列的remove方法会一直阻塞到有引用加入队列;因此借助引用队列的remove可以在对象内存被释放前做一些业务,比喻获取对象释放的时间;需要指出来的时,因为虚引用get方法永远返回null,所以在对象内存被释放时,就不会出现自定义对象的finalize时逃逸回收的问题;
不过也不绝对,通过引用对象反射依旧有办法可以获取到引用的对象,但是这是非常规操作,基本可以忽略;感觉这虚引用没啥用;

感谢 Relucent 的例子:

https://github.com/Relucent/yyl_example/blob/master/src/main/java/yyl/example/basic/reference/PhantomReferenceTest.java

我将例子修了一下:

final ReferenceQueue<Person> queue = new ReferenceQueue<>();
Person person = new Person("John");

new Thread() {
    public void run() {
        try {
//会一直阻塞直到`虚引用`加到队列中;
Reference<Person> reference = (Reference<Person>) queue.remove();
System.out.println("垃圾回收器, 准备回收对象: " + reference.get());

System.out.println("垃圾回收器, 准备回收对象(暴力获取): " + forceGet(reference));

        } catch (InterruptedException e) {				      
e.printStackTrace();
        }
}}.start();

PhantomReference<Person> reference = new PhantomReference<>(person, queue);

//虚引用 get 永远返回 null		
System.out.println("reference.get() -> " + reference.get());

//直接读取引用字段
System.out.println("reference.referent -> " + forceGet(reference));

Thread.sleep(2000);

person = null;

System.gc();

执行结果:

reference.get() -> null
reference.referent -> Person{name='John'}
垃圾回收器, 准备回收对象: null
垃圾回收器, 准备回收对象(暴力获取): Person{name='John'}

参考内容:

http://blog.csdn.net/xiaofengcanyuexj/article/details/45271195
http://www.infoq.com/cn/articles/cf-java-garbage-references
http://droidyue.com/blog/2014/10/12/understanding-weakreference-in-java/index.html

Read more

无题

看着川川熟睡的样子,看着他俩在我面前嬉闹的跑过,我总感觉特别恍惚; 不知如何和他们相处,甚至也不知如何与不知如何是好的自己相处; 于是我只好查查知乎学样子,学着做好一个为人父的样子; 在心情舒畅的时候,我放任他们自己翻箱倒柜、在地上躺水打滚、接住他们发出的各种听不懂的咿咿呀呀,想的是让他们洒脱自由地探索这个世界,给予最积极的回应;心情烦躁到收不住火的时候,也免不了大吼几声,事后愧疚不已,接应他们的目光时也有些闪躲; 如何陪伴他们让他们能够适应这个不完美的世界?如何让自己撇去浮躁能够自如的处理这一切? 我想:生命本来就是荒凉的,生活本来就是凛冽的; 人生很长,余生漫漫; 令人心烦的工作……日渐老去的父母……还有各种空洞又悠长的忧伤…… 我想我终究对他俩还是有期待的,期待他们有明媚的性格,健硕的人格,充盈的内心,潇洒的生活; 人生很短,倏然过半; 每一天都是旧的,每一天又都是新的; 以为没什么可怀念的过去,过去却时常冒出来……总想着的未来,未来却已到来;这个世界本没有完美的事,不如意总是常态; 希望我们都够心存温柔、感念过往、一起寻找美好;

By Jiacheng Wang

无题

萦绕在我记忆里的老家两层楼房,总是二层左侧还没有盖起来的样子。经过危险的没有扶手的楼梯才能上二层。 不知为何:在我无数次的梦里,楼梯和二层玉制板总是摇晃不止,我虽胆战心惊却仍旧不知疲倦的跑上跑下。 小时候的美味在我的味蕾深处,从未遗忘。 夏天里的丝瓜、长豆角、苦苦的小萝卜菜……母亲做坏了长虫子的豆豉酱、到年中还有的腊鱼、腊肉;雪白雪白的猪油炒饭;豆糕、糍粑、薯片; 尤其记得薯片的做法:把红薯去皮洗干净和糯米一起蒸熟,加入橘子皮,揣成泥状,均匀摊薄然后糊在被子面上晒干,再从被子面上撕下来,成一层半透明的硬胶状的干薯皮,用剪刀剪成菱形、平行四边形的形状储存起来。吃的时候就用热沙炒熟成砖红色,吃起来夹着淡淡的橘皮香味,淡甜香脆; 5毛钱两块的麻酥、一升米加一包糖精炸出来的乐口笑……都是晚上不吃完不睡觉零嘴; 小时候村里有人家盖房子,会有大货车来送砖,我总是和村里小伙伴一起给人家搬砖,搬一车砖换取几毛钱的劳动成果;带着一身脏兮兮的衣服回家,总免不了母亲几声轻柔的责备; 但记忆里母亲从来没有责骂过我,即便教育我的时候,也只提是反面教材的别人家的孩子,于是我就知道像那样就不对; 老家里

By Jiacheng Wang

无题

小时候的记忆现在已经模糊了,即便有一些清晰的我想应该也仅是自己在某一些记忆碎片上『添油加醋』拼凑而成的; 我的父亲在家排行第三,前面是两个哥哥,我的大伯和二伯;家里穷只有一幢土砖房,到父亲娶我母亲的时候,只能和大伯一家共用那幢老房子;我家在房子左侧,大伯家在房子右侧,共用一个大门进入;整幢房子有几居室已经不记得了,只记得进我家屋门是一个卧室,卧室后面是厨房,而我的人生记忆放佛也是从这厨房开始的; 相比做素菜,母亲较擅长会做荤菜,那个年头的荤菜是罐子煨排骨汤、煨鸡汤;很简单粗矿的做法,罐子煨排骨汤是将盛满排骨肉加汤料的罐子放在将要烧尽的灶膛里,慢慢煨;往往是就着晚餐的灶火做,第二天吃,煨鸡汤也是同样的道理;最开心的要数快到过年的腊月二十八了,老家称这一天为还福;这一天,大家会早早在四五点就起床,父亲母亲往往会比我和妹妹起得更早,他们要忙碌祭祖的用具,在祭祖磕头的时候才叫我和妹妹起床;而在老屋里的祭祖环节已经不记得了,但记得一家四口围坐在厨房的餐桌上吃还福这一天的早晨大餐,烧豆腐、青蒜炒腊肉、油煎腊鱼干、鱼丸、肉丸、再次热好头天晚上煨好的排骨汤和鸡汤;记忆里的罐子煨好的汤上总漂着零星黑

By Jiacheng Wang

一些思考

做为系统架构师入职有一段时间了,虽然磕磕绊绊,总体来说还算有一些进步; 这里也梳理一些思考,既做总结,也为后面行事方式提出一些想法; 首先就是业务研发人员和架构师的工作职责区分; * 业务研发人员,工作职责倾向于业务需求实现,着眼于项目如期、有质量交付; * 架构师,工作职责倾向于业务需求确认,评估业务当前的、未来可能的演进状况,给出合理的系统画分、边界职责、开发规范;着眼于提高团队研发效率、保证系统的可扩展性; 一度我还是一厢情愿的想二者不应独立来看,无论从个人成长、团队整体绩效,二者应该职责合一;后来慢慢发现现实很骨干;这其中的原因,我总结如下: * 人各不同,理解问题的视角、层次不尽相同,也无法苛求一致; * 业务需求实现研发和架构合理化演进在某些层面的认知可能存在冲突; 有的人意愿沿用规则,有的人意愿创造规则;有的人被动适应变化,有的人能主动创建变化;个人的视野、思考问题的深度是受限于个人的认知;对待团队成员我们不能像对产品一样统一规格要求;这里不展开叙述; 业务需求研发强调按时按量交付,架构合理化强调研发提效和未来扩展可能,二者在某些层面可能带来冲突;症结

By Jiacheng Wang