在java中提供4个级别的引用:强引用、软引用、弱引用和虚引用。除了强引用外,其他3中引用均可以在java.lang.ref包中找到对应的类。开发人员可以在应用程序中直接使用他们。

1 强引用

强引用就是程序中一般使用的引用类型,强引用的对象是可触及的,不会被回收。相对的,软引用、弱引用和虚引用的对象是软可触及的、弱可触及的和虚可触及的,在一定条件下,都是可以被回收的。

强引用示例:StringBuffer str = new StringBuffer("Hello world");

假设以上代码是在函数体内运行的,那么局部变量str将被分配在栈上,而对象StringBuffer实例被分配在堆上。局部变量str指向StringBuffer实例所在堆空间,通过str可以操作该实例,那么str就是StringBuffer实例的强引用。

此时,如果再运行一个赋值语句:

StringBuffer str1 = str;

则str所指向的对象也将被str1所指向,同时在局部变量表上会分配空间存放str1变量。此时该StringBuffer实例就有两个引用。对引用的“==”操作用于表示两操作数所指向的堆空间地址是否相同,不表示两操作数所指的对象是否相同。

强引用的特点:

  • 强引用可以直接访问目标对象
  • 强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指的对象。
  • 强引用可能导致内存泄漏

2 软引用----可被回收的引用

软引用是比强引用弱一点的引用类型,一个对象只持有软引用,那么当堆空间不足时,就会被回收。软引用使用java.lang.ref.SoftReference类型

package com.jvm;
import java.lang.ref.SoftReference;
public class SoftRef {
  public static class User{
    public User(int id,String name){
      this.id = id;
      this.name = name;
    }
    public int id;
    public String name;
    @Override
    public String toString() {
      return "[id="+String.valueOf(id)+",name="+name+"]";
    }
  }
  public static void main(String[] args) {
    User u = new User(1,"gim");
    SoftReference<User> userSoftRef = new SoftReference<User>(u);//创建引用给定对象的新的软引用
    u = null; //去除强引用

    System.out.println(userSoftRef.get());
    System.gc();
    System.out.println("After GC:");
    System.out.println(userSoftRef.get());

    byte[] b = new byte[1024*925*7];//分配7M左右,使内存空间紧张
    System.gc();
    System.out.println(userSoftRef.get());
  }
}

如果虚拟机不加参数设置堆内存限制,则打印出:

[id=1,name=gim]
After GC:
[id=1,name=gim]
[id=1,name=gim]

因为虚拟机的内存总量是九十多兆,且虚拟机企图使用的最大内存总量是这个的十几倍。故不会出现资源紧张,也不会产生内存溢出现象。

如果使用-Xmx10m运行上述代码,可以得到:

[id=1,name=gim](第一次从软引用中获取数据)
After GC:
[id=1,name=gim](GC没有清除软引用)
null(由于内存紧张,软引用被清除)

结论:GC未必会回收软引用的对象,但是当内存资源紧张时,软引用对象会被回收,所以软引用对象不会引起内存溢出。

3 弱引用-----发现即回收

弱引用是一种比软引用较弱的引用类型。在系统GC时,只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。但是由于垃圾回收器的优先级通常很低,因此,并不一定能很快的发现持有弱引用的对象。在这种情况下,弱引用对象可以存在较长的时间,一旦一个弱引用对象被垃圾回收器回收,便会加入一个注册的引用队列(这一点和软引用很像)。弱引用使用java.lang.ref.WeakReference类实现。

实例2:展示弱引用的特点

package com.jvm;
import java.lang.ref.WeakReference;
public class WeakRef {
  public static class User{
    public User(int id,String name){
      this.id = id;
      this.name = name;
    }
    public int id;
    public String name;

    @Override
    public String toString() {
      return "[id="+String.valueOf(id)+",name="+name+"]";
    }
  }

  public static void main(String[] args) {
    User u = new User(1,"gim");
    WeakReference<User> userReference = new WeakReference<User>(u);
    u = null;
    System.out.println(userReference.get());
    System.gc();
    System.out.println("After GC:");
    System.out.println(userReference.get());
  }
}

不用设置虚拟机参数,直接运行代码打印出:

[id=1,name=gim]
After GC:
null

可以看书,GC之后,弱引用对象立即被清除。弱引用和软引用一样,在构造弱引用时,也可以指定一个引用队列,当弱引用对象被回收时,就会加入指定的引用队列,通过这个队列可以跟踪对象的回收情况。

注意:软引用、弱引用都非常适合来保存那些可有可无的缓存数据(应用场景)。如果这么做,当系统内存不足时,这些缓存数据会被回收,不会导致内存的溢出,而当内存资源充足时,这些缓存又可以存在相当长的时间,从而起到加速系统的作用。

4 虚引用----对象回收跟踪

一个持有虚引用的对象,和没有引用几乎一样。随时都可能被垃圾回收器回收,当试图通过虚引用的get()方法取得强引用时,总是会失败。

并且,虚引用必须和引用队列一起使用,它的作用主要是跟踪垃圾回收过程

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知引用程序对象的回收情况。

实例3 :使用虚引用跟踪一个可复活对象的回收

package com.jvm;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class TraceCanReliveObj {
  public static TraceCanReliveObj obj;  //可以复活对象
  static ReferenceQueue<TraceCanReliveObj> phantomQueue = null;
  public static class CheckRefQueue extends Thread{
    @Override
    public void run() {
      while(true){
        if(phantomQueue!=null){
          PhantomReference<TraceCanReliveObj> objt = null;
          try{
            objt = (PhantomReference<TraceCanReliveObj>) phantomQueue.remove();
          }catch(InterruptedException e){
            e.printStackTrace();
          }
          if(objt!=null){
            System.out.println("TraceCanReliveObj is delete by GC");
          }
        }
      }
    }
  }

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("CanReliveObj finalize called");
    obj = this; //使得对象复活
  }
  @Override
  public String toString() {
    return "I am CanReliveObj";
  }
  public static void main(String[] args) throws InterruptedException {
    Thread t = new CheckRefQueue();
    t.setDaemon(true);//守护线程
    t.start();
    phantomQueue = new ReferenceQueue<TraceCanReliveObj>();
    obj = new TraceCanReliveObj();

    //构造了TraceCanReliveObj对象的虚引用,并指定了引用队列。
    PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj, phantomQueue);
    obj = null;  //将强引用去除
    System.gc();  //第一次进行GC,由于对象可复活,GC无法回收该对象。
    Thread.sleep(1000);
    if(obj==null){
      System.out.println("obj 是 null");
    }else{
      System.out.println("obj 可用");
    }
    System.out.println("第二次GC");
    obj = null;
    System.gc(); //第二次进行GC,由于finalize()只会被调用一次,因此第2次GC会回收对象,同时其引用队列应该也会捕获到对象的回收
    Thread.sleep(1000);
    if(obj==null){
      System.out.println("obj 是 null");
    }else{
      System.out.println("obj 可用");
    }
  }
}

直接运行代码打印:

CanReliveObj finalize called    (对象复活)
obj 可用
第二次GC           (第2次,对象无法复活) 
TraceCanReliveObj is delete by GC (引用队列捕获到对象被回收)
obj 是 null

由于虚引用可以跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虚引用中执行和记录。

Java的四种引用之强弱软虚的更多相关文章

  1. Java的四种引用,强弱软虚,用到的场景

    众所周知,java中是JVM负责内存的分配和回收,这是它的优点(使用方便,程序不用再像使用c那样操心内存),但同时也是它的缺点(不够灵活).为了解决内存操作不灵活这个问题,可以采用软引用等方法. 在J ...

  2. java的四种引用,强弱软虚和jvm优化

    1.强引用(StrongReference)强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.如下: Object o=new Object();   //  强引用 当内存 ...

  3. java的四种引用,强弱软虚

    1.利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的 ...

  4. Java中四种引用:强、软、弱、虚引用

    这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...

  5. Java基础:Java的四种引用

    在Java基础:java虚拟机(JVM)中,我们提到了Java的四种引用.包括:强引用,软引用,弱引用,虚引用.这篇博客将详细的讲解一下这四种引用. 1. 强引用 2. 软引用 3. 弱引用 4. 虚 ...

  6. Java的四种引用方式

    一.引用基本概念 从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期.这四种级别由高到低依次为:强引用.软引用.弱引用.虚引用. 1.强引用(StrongRef ...

  7. Java虚拟机(五)Java的四种引用级别

    1.前言 HotSpot采取了可达性分析算法用来判断对象是否被能被GC,无论是引用计算法还是可达性分析算法都是判断对象是否存在引用来判断对象是否存活.如果reference类型的数据中存储的数值代表的 ...

  8. java gc --- 四种引用

    古龙有<七种武器>,java里有四种引用. 下文主要是对 <understanding-weak-references>这一博文的重点进行翻译 强引用,strong refer ...

  9. [转载] Java的四种引用关系

    目录 1 强引用 (Final Reference) 2 软引用 (Soft Reference) 2.1 案例1: 软引用的垃圾回收 2.2 案例2: 软引用缓存的使用 2.3 软引用的应用场景 3 ...

随机推荐

  1. python-day47--mysql数据备份与恢复

    一.IDE工具介绍 掌握: #1. 测试+链接数据库 #2. 新建库 #3. 新建表,新增字段+类型+约束 #4. 设计表:外键 #5. 新建查询 #6. 备份库/表 #注意: 批量加注释:ctrl+ ...

  2. Leetcode 78

    //和77类似的问题,可以放在一起记忆class Solution { public: vector<vector<int>> subsets(vector<int> ...

  3. textAlign

    <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head>    < ...

  4. iOS开发-开发文档安装

    iOS开发肯定离不开开发文档,苹果有在线帮助文档,xCode其实可以下载模拟器文档和iOS8.1文档的,不过下载的速度实在不敢恭维,而且比较头疼的是不显示下载进度条的,苹果的开发文档都是放在)/应用程 ...

  5. Unicode与UTF-8,UTF-16

    Unicode(UTF-8, UTF-16)令人混淆的概念 为啥需要Unicode 我们知道计算机其实挺笨的,它只认识0101这样的字符串,当然了我们看这样的01串时肯定会比较头晕的,所以很多时候为了 ...

  6. bacula配置

    Bacula Bacula是一款开源的跨平台网络备份工具,提供基于企业级的CS的备份解决方案.可以对数据进行备份.恢复.以及完整性校验.  功能特点: 支持完全备份,增量备份,差异备份. 支持多种恢复 ...

  7. 仿sql注入 sql

    <?phpclass sqlsafe { //(and|or)\\b 表示以and和or结尾的单词如:aand,band,都可以匹配//如果匹配and或or则使用 \\b(and|or)\\b来 ...

  8. 向IOS模拟机上添加图片

    [问题] 折腾: 关于iOS/iPhone中的文件选择对话框,用于用户去选择图片等文件 的过程中,遇到个问题,希望程序提供用户选择自己想要的图片作为Bird的图片. 但是当前开发环境是iOS模拟器,所 ...

  9. Redis学习第三课:Redis Hash类型及操作

    Redis hash是一个string类型的field和value的映射表.它的添加.删除操作都是O(1)(平均).hash特别适用于存储对象.相较于对象的每个字段存在单个string类型.将一个对象 ...

  10. OC基础:getter和setter,@public @protected @private 分类: ios学习 OC 2015-06-15 19:23 22人阅读 评论(0) 收藏

    @public 1.公开的,公共的,可以在类的内部和外部访问. 2.类的内部:实例变量名 3.类的外部:对象->实例变量名 @protected 1.受保护的,只能在本类和子类中可以访问 2.类 ...