强引用,软引用,弱引用,虚引用。
用了Java怎么长时间一直不知道原来Java还有四种引用类型,这个引用类型和我们平常说的可不一样。这里的引用类型不是指数据类型的一种,而是指Java中的引用所分的四种类型。他们代表了JVM回收内存的四种强度,分别如下。
强引用:
Java中的引用,有点像C++的指针。通过引用,可以对堆中的对象进行操作。在某函数中,当创建了一个对象,该对象被分配在堆中,通过这个对象的引用才能对这个对象进行操作。
- Object o=new Object();
假设以上代码是在函数体内运行的,那么局部变量o将被分配在栈上,而对象实例,被分配在堆上。局部变量o指向Object实例所在的堆空间,通过o可以操作该实例,那么o就是Object的引用。
如果再创建一个赋值语句
- Object oj=o;
- public class Test1 {
- public static void main(String[] args) {
- student S=new student("1","我是大s");
- student s=S;
- System.out.println(s==S); //true
- System.out.println(s.equals(S)); //true
- System.out.println("打印出大S"+S); //id=1
- System.out.println("打印出小s"+s); //id=1
- S.setId("2");
- System.out.println("修改后的大S"+S); //id=2
- System.out.println("未修改后的小s"+s); //id=2
- s.setId("3");
- System.out.println("未修改后的大S"+S); //id=3
- System.out.println("修改后的小s"+s); //id=3
- }
- }
- class student{
- String name;
- String id;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public student(String name, String id) {
- super();
- this.name = name;
- this.id = id;
- }
- @Override
- public String toString() {
- return "student [name=" + name + ", id=" + id + "]";
- }
- }
上例中的大S和小S都是强引用,强引用具有如下特点:
1.强引用可以直接访问目标对象。
2.强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。
3.强引用可能导致内存泄漏,为了避免内存泄漏,在使用完成之后我们可以把字符串对象设置为null,如果是集合的话可以使用
- List list=new ArrayList<>();
- list.clear();
软引用:
软引用的强度是仅次于强引用的,如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
我们可以使用java.lang.ref.SoftReference来创建软引用;
- String str=new String("abc"); // 强引用
- SoftReference<String> softRef=new SoftReference<String>(str); // 软引用
- If(JVM.内存不足()) {
- str = null; // 转换为软引用
- System.gc(); // 垃圾回收器进行回收
- }
弱引用:
弱引用的强度比软引用更次,也就是说只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来标记此对象。
- String str=new String("abc");
- WeakReference<String> abcWeakRef = new WeakReference<String>(str);
- str=null;
- String abc = abcWeakRef.get();
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候你就是用弱引用。
这个引用不会在对象的垃圾回收判断中产生任何附加的影响。
- public class ReferenceTest {
- private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();
- public static void checkQueue() {
- Reference<? extends VeryBig> ref = null;
- while ((ref = rq.poll()) != null) {
- if (ref != null) {
- System.out.println("In queue: " + ((VeryBigWeakReference) (ref)).id);
- }
- }
- }
- public static void main(String args[]) {
- int size = 3;
- LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
- for (int i = 0; i < size; i++) {
- weakList.add(new VeryBigWeakReference(new VeryBig("Weak " + i), rq));
- System.out.println("Just created weak: " + weakList.getLast());
- }
- System.gc();
- try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
- Thread.currentThread().sleep(6000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- checkQueue();
- }
- }
- class VeryBig {
- public String id;
- // 占用空间,让线程进行回收
- byte[] b = new byte[2 * 1024];
- public VeryBig(String id) {
- this.id = id;
- }
- protected void finalize() {
- System.out.println("Finalizing VeryBig " + id);
- }
- }
- class VeryBigWeakReference extends WeakReference<VeryBig> {
- public String id;
- public VeryBigWeakReference(VeryBig big, ReferenceQueue<VeryBig> rq) {
- super(big, rq);
- this.id = big.id;
- }
- protected void finalize() {
- System.out.println("Finalizing VeryBigWeakReference " + id);
- }
- }
虚引用
对比不同:
- class Grocery {
- private static final int SIZE = 10000;
- // 属性d使得每个Grocery对象占用较多内存,有80K左右
- private double[] d = new double[SIZE];
- private String id;
- public Grocery(String id) {
- this.id = id;
- }
- public String toString() {
- return id;
- }
- public void finalize() {
- System.out.println("即将回收 " + id);
- }
- }
- public class References {
- private static ReferenceQueue<Grocery> rq = new ReferenceQueue<Grocery>();
- public static void checkQueue() {
- Reference<? extends Grocery> inq = rq.poll(); // 从队列中取出一个引用
- if (inq != null)
- System.out.println("In queue: " + inq + " : " + inq.get());
- }
- public static void main(String[] args) {
- final int size = 10;
- // 创建10个Grocery对象以及10个软引用
- Set<SoftReference<Grocery>> sa = new HashSet<SoftReference<Grocery>>();
- for (int i = 0; i < size; i++) {
- SoftReference<Grocery> ref = new SoftReference<Grocery>(
- new Grocery("软引用 " + i), rq);
- System.out.println("刚刚 创建了: " + ref.get());
- sa.add(ref);
- }
- System.gc();
- checkQueue();
- // 创建10个Grocery对象以及10个弱引用
- Set<WeakReference<Grocery>> wa = new HashSet<WeakReference<Grocery>>();
- for (int i = 0; i < size; i++) {
- WeakReference<Grocery> ref = new WeakReference<Grocery>(
- new Grocery("弱引用 " + i), rq);
- System.out.println("刚刚 创建了: " + ref.get());
- wa.add(ref);
- }
- System.gc();
- checkQueue();
- // 创建10个Grocery对象以及10个虚引用
- Set<PhantomReference<Grocery>> pa = new HashSet<PhantomReference<Grocery>>();
- for (int i = 0; i < size; i++) {
- PhantomReference<Grocery> ref = new PhantomReference<Grocery>(
- new Grocery("abc " + i), rq);
- System.out.println("刚刚 创建了: " + ref.get());
- pa.add(ref);
- }
- System.gc();
- checkQueue();
- }
- }
总结
软引用:有点像老板(OOM)的亲戚,在公司表现不好有可能会被开除,即使你投诉他(调用GC)上班看片,但是只要不被老板看到(被JVM检测到)就不会被开除(被虚拟机回收)。
弱引用:就是一个普通的员工,平常如果表现不佳会被开除(对象没有其他引用的情况下),遇到别人投诉(调用GC)上班看片,那开除是肯定了(被虚拟机回收)。
虚引用:这货估计就是个实习生跟临时工把,遇到事情的时候想到了你,没有事情的时候,秒秒钟拿出去顶锅,开除。