[Java开采之路](25)引用类型

[Java开荒之路](25)援引类型

正文为阅读周志明先生《深远通晓 Java
虚拟机》一书时的读书笔记,倘令你想询问有关虚构机的一点一滴,推荐阅读该书。

1. 强引用

Java中的引用,类似于C++的指针。通过援引,能够对堆中的对象进行操作。在某些函数中,当创立了四个目的,该目标被分配在堆中,通过那几个目的的引用技艺对那一个目的开始展览操作。

  1. StringBuffer str = new StringBuffer("hello world");

假使以上代码是在函数体内运维的,那么一些变量str将被分配在栈上,而指标StringBuffer实例,被分配在堆上。局地变量str指向StringBuffer实例所在的堆空间,通过str能够操作该实例,那么str正是StringBuffer的援用。

亚洲必赢app 1

这时,启动二个赋值语句:

  1. StringBuffer str1 = str;

那么,str所针对的对象也将被str1所指向,同期在有个别栈空间上会分配空间贮存str1变量。此时,该StringBuffer实例就有多个引用。对援引的”==”操作用于表示八个操作数所针对的堆空间地址是或不是一致,不意味着四个操作数所针对的对象是或不是等于。

亚洲必赢app 2

强引用特点:

强引用能够一贯访问指标对象。

强援用所指向的目的在其余时候都不会被系统回收。JVM宁愿抛出OOM相当,也不会回收强援用所针对的对象。

强援引恐怕导致内部存款和储蓄器走漏。

 

该节首要介绍 Java 中指标的四中差别引用关系。

2. 软引用

软援用是除了强引用外,最强的援引类型。能够通过java.lang.ref.SoftReference使用软引用。三个具有软引用的指标,不会被JVM非常的慢回收,JVM会遵照当下堆的利用状态来判定曾几何时回收。当堆的使用率周边阈值时,才会回收软征引的指标。

  1. package com.qunar.base;
  2. import java.lang.ref.Reference;
  3. import java.lang.ref.ReferenceQueue;
  4. import java.lang.ref.SoftReference;
  5. /**
  6. * Created by xiaosi on 16-3-24.
  7. */
  8. public class ReferenceDemo {
  9. // 创建引用队列
  10. private ReferenceQueue referenceQueue = new ReferenceQueue<>();
  11. public class MyObject {
  12. @Override
  13. protected void finalize() throws Throwable {
  14. super.finalize();
  15. // 被回收时输出
  16. System.out.println("MyObject is finalize called");
  17. }
  18. @Override
  19. public String toString() {
  20. return " I am MyObject";
  21. }
  22. }
  23. public class CheckRefQueue implements Runnable {
  24. Reference obj = null;
  25. @Override
  26. public void run() {
  27. try {
  28. // 如果对象被回收则进入引用队列
  29. obj = (Reference) referenceQueue.remove();
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. if (obj != null) {
  34. System.out.println("Object for SoftReference is " + obj.get());
  35. }
  36. }
  37. }
  38. public void test() {
  39. // 创建强引用
  40. MyObject myObject = new MyObject();
  41. // 构造myObject对象的软引用 注册到 引用队列
  42. SoftReference softReference = new SoftReference<>(myObject, referenceQueue);
  43. CheckRefQueue checkRefQueue = new CheckRefQueue();
  44. Thread thread = new Thread(checkRefQueue);
  45. thread.start();
  46. // 删除强引用 对myObject对象的引用只剩下软引用
  47. myObject = null;
  48. System.gc();
  49. System.out.println("After GC: Soft Get = " + softReference.get());
  50. System.out.println("分配大块内存");
  51. // 分配一块强大的内存,强迫GC
  52. byte[] b = new byte[5*1024*963];
  53. System.out.println("After new byte[]:Soft Get = "+softReference.get());
  54. System.gc();
  55. }
  56. public static void main(String[] args) {
  57. ReferenceDemo referenceDemo = new ReferenceDemo();
  58. referenceDemo.test();
  59. }
  60. }

先是构造MyObject对象,并将其赋值给myObject变量,构成强援用。然后利用SoftReference构造那一个MyObject对象的软援引softReference,并注册到referenceQueue队列中。当softReference被回收时,会被参预referenceQueue队列。设置myObject=null,删除那一个强援用,由此,系统内对MyObject对象的引用只剩下软引用。此时,显示调用GC,通过软援引的get()方法,取得MyObject对象实例的强引用,开掘目的被未回收。那表明GC在内部存储器丰裕的情事下,不会回收软援引对象。

 

亚洲必赢app,随即,央浼一块大的堆空间new
byte[4*1024*925],这些操作会使操作系统内部存储器使用恐慌,进而发生新一轮的GC。本次GC后,softReference.get()不再回到MyObject对象,而是重临null,表达在系统内部存款和储蓄器恐慌的情事下,软援用被回收。软引用被回收时,会被参预到注册的援用队列中。

备注:

JVM参数:-Xmx5M

运作结果:

/opt/jdk1.7.0_40/bin/java-Xmx5M…

After GC: Soft Get = I am MyObject

分中国工人和农民红军政大学学块内存

After new byte[]:Soft Get = null

MyObject is finalize called

Object for SoftReference is null

 

关于援用

正如,是一个轻巧的 Java 代码。

class Test{ 
  public static void main(String[]args){
    Person p = new Person();
    p.name = "mao";
  } 
}

能够看出,p 为八个 Person 对象的援用。
当今让大家思想一下那行代码在 JVM 中运作时内存的分配意况。

此处的分配分为两步:
1、依照 new 这几个命令,JVM 会在 Java 堆中创设贰个具体的 Person 对象。
2、把刚分配的 Person 对象在堆中的’地址值’贮存在 Java
虚构机栈中,并钦命给 p。

到此,三个援用关系就早就确立好了。如下图所示。

亚洲必赢app 3

{650E4688-E269-26E0-65FA-B37836269737}.png

自此全数对堆中十二分 Person 对象的访问,都通过 p 访谈,也正是老大援用。

今昔这种暗中同意的引用类型便是下边将在聊起的强引用关系。在这种引用关系的前提下,当使用内部存款和储蓄器不足,JVM
发生垃圾回收时,位于堆内部存款和储蓄器中的 Person 对象将不会被释放掉。
上面具体介绍一下,Java 中分化的引用类型。

从 JDK 1.2 之后,Java 将援用分为强引用(Strong Reference)、软引用(Soft
Reference)、弱援用(Weak Reference)、虚引用(Phantom
Reference)七种,那各样引用强度依次逐级减少。

3. 弱引用

弱援引是一种比软援用较弱的援引类型。在系统GC时,只要发觉弱援用,不管系统堆空间是还是不是丰硕,都会将目的开始展览回收。可是,由于废品回收器的线程平常事先级十分低,由此,并一不定能比十分的快的意识具备弱援引的靶子。这种气象下,弱援引对象能够存在较长的一段时间。一旦多少个弱引用对象被垃圾回收器回收,便会插足到叁个注册援引队列中。

  1. package com.qunar.base;
  2. import java.lang.ref.Reference;
  3. import java.lang.ref.ReferenceQueue;
  4. import java.lang.ref.SoftReference;
  5. import java.lang.ref.WeakReference;
  6. /**
  7. * Created by xiaosi on 16-3-24.
  8. */
  9. public class WeakReferenceDemo {
  10. // 创建引用队列
  11. private ReferenceQueue referenceQueue = new ReferenceQueue<>();
  12. public class MyObject {
  13. @Override
  14. protected void finalize() throws Throwable {
  15. super.finalize();
  16. // 被回收时输出
  17. System.out.println("MyObject is finalize called");
  18. }
  19. @Override
  20. public String toString() {
  21. return " I am MyObject";
  22. }
  23. }
  24. public class CheckRefQueue implements Runnable {
  25. Reference obj = null;
  26. @Override
  27. public void run() {
  28. try {
  29. // 如果对象被回收则进入引用队列
  30. obj = (Reference) referenceQueue.remove();
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. if (obj != null) {
  35. System.out.println("Object for SoftReference is " + obj.get());
  36. }
  37. }
  38. }
  39. public void test() {
  40. // 创建强引用
  41. MyObject myObject = new MyObject();
  42. // 构造myObject对象的弱引用 注册到 引用队列
  43. WeakReference weakReference = new WeakReference<>(myObject, referenceQueue);
  44. CheckRefQueue checkRefQueue = new CheckRefQueue();
  45. Thread thread = new Thread(checkRefQueue);
  46. thread.start();
  47. // 删除强引用 对myObject对象的引用只剩下弱引用
  48. myObject = null;
  49. System.out.println("Before GC: Weak Get = " + weakReference.get());
  50. System.gc();
  51. System.out.println("After GC: Weak Get = " + weakReference.get());
  52. }
  53. public static void main(String[] args) {
  54. WeakReferenceDemo referenceDemo = new WeakReferenceDemo();
  55. referenceDemo.test();
  56. }
  57. }
  • 运维结果:

    Before GC: Weak Get = I am MyObject

    After GC: Weak Get = null

    MyObject is finalize called

    Object for SoftReference is null

     

    在GC以前,弱引用对象并未被垃圾回收器开采,因而通过weakRef.get()方法能够博得相应的强援用。不过如果进行垃圾回收,弱援用对象一旦被察觉,便会应声被回收,并走入注册引用队列中。此时,再度经过weakRef.get()方法赢得强援用就能失利。

     

    备注:

    软援用,弱引用都特别适合来保存那个可有可无的缓存数据。要是那样做,当系统内存不足时,那个缓存数据会被回收,不会产生内部存储器溢出。而当内部存款和储蓄器能源丰裕时,那几个缓存数据又能够存在一定长的大运。

     

    #### 4. 虚引用

    虚援用是持有援用类型中最弱的三个。一个独具虚援用的指标,和尚未引用大致是一致的,随时都恐怕被垃圾回收器回收。当试图透过虚援用的get()方法赢得强援引时,总是会失败。而且,虚引用必须和引用队列一齐使用,它的意义在于追踪垃圾回收过程。

    当废品回收器绸缪回收叁个指标时,假若发掘它还会有虚援引,就能在垃圾回收后,销毁这些指标,奖那些虚援引投入援引队列。

    1. package com.qunar.base;
    2.  
    3. import java.lang.ref.PhantomReference;
    4. import java.lang.ref.Reference;
    5. import java.lang.ref.ReferenceQueue;
    6. import java.lang.ref.WeakReference;
    7.  
    8. /**
    9. * Created by xiaosi on 16-3-24.
    10. */
    11. public class PhantomReferenceDemo {
    12. // 创建引用队列
    13. private ReferenceQueue referenceQueue = new ReferenceQueue<>();
    14.  
    15. public class MyObject {
    16. @Override
    17. protected void finalize() throws Throwable {
    18. super.finalize();
    19. // 被回收时输出
    20. System.out.println("MyObject is finalize called");
    21. }
    22.  
    23. @Override
    24. public String toString() {
    25. return " I am MyObject";
    26. }
    27. }
    28.  
    29. public class CheckRefQueue implements Runnable {
    30. Reference obj = null;
    31.  
    32. @Override
    33. public void run() {
    34. try {
    35. // 如果对象被回收则进入引用队列
    36. obj = (Reference) referenceQueue.remove();
    37. // 等待 直到取得虚引用对象
    38. System.out.println("Object for PhantomReference is " + obj.get());
    39. System.exit(0);
    40. } catch (InterruptedException e) {
    41. e.printStackTrace();
    42. }
    43. if (obj != null) {
    44. System.out.println("Object for SoftReference is " + obj.get());
    45. }
    46. }
    47. }
    48.  
    49. public void test() throws InterruptedException {
    50. // 创建强引用
    51. MyObject myObject = new MyObject();
    52. // 构造myObject对象的虚引用 注册到 引用队列
    53. PhantomReference phantomReference = new PhantomReference<>(myObject, referenceQueue);
    54.  
    55. System.out.println("phantomReference Get : " + phantomReference.get());
    56.  
    57. CheckRefQueue checkRefQueue = new CheckRefQueue();
    58. Thread thread = new Thread(checkRefQueue);
    59. thread.start();
    60.  
    61. // 删除强引用 对myObject对象的引用只剩下虚引用
    62. myObject = null;
    63. Thread.sleep(1000);
    64.  
    65. int i = 1;
    66.  
    67. while(true){
    68. System.out.println("第" + i + "次GC");
    69. System.gc();
    70. Thread.sleep(1000);
    71. i++;
    72. }//while
    73. }
    74.  
    75. public static void main(String[] args) throws InterruptedException {
    76. PhantomReferenceDemo referenceDemo = new PhantomReferenceDemo();
    77. referenceDemo.test();
    78. }
    79. }

    运作结果:

    phantomReference Get : null

    第1次GC

    MyObject is finalize called

    第2次GC

    Object for PhantomReference is null

     

    从那几个输出结果中得以见见,对虚援引的get()操作,总是回到null,固然强引用还留存时,也不例外。因为虚援引的get()完毕:

    1. public T get() {
    2. return null;
    3. }

    在首先次GC时,系统找到了垃圾堆对象,并调用其finalize()方法回收内部存款和储蓄器,但从没即时到场到回收队列中。第一遍GC时,该对象真正的被GC清除,此时,加入虚援用队列。

     

    虚引用最大效果与利益在于追踪对象回收,清理被销毁对象的相关能源。常常,当对象不被使用时,重载该类的finalize()方法能够回收该对象的财富。可是,借使finalize()方法运用不慎,或者导致该目的复活。

    上面看贰个荒唐的finalize()完结的例证,这么些实现导致内部存款和储蓄器溢出,或然指标永久无法被回收。

     

    1. package com.qunar.base;
    2.  
    3. /**
    4. * Created by xiaosi on 16-3-24.
    5. */
    6. public class PhantomObject {
    7. public static PhantomObject phantomObject;
    8. @Override
    9. protected void finalize() throws Throwable {
    10. super.finalize();
    11. // 被回收时输出
    12. System.out.println("MyObject is finalize called");
    13. // 在finalize()中拯救了将要被回收的对象
    14. phantomObject = this;
    15. }
    16.  
    17. @Override
    18. public String toString() {
    19. return " I am MyObject";
    20. }
    21.  
    22. public static void main(String[] args) throws InterruptedException {
    23. phantomObject = new PhantomObject();
    24. // 删除对象
    25. phantomObject = null;
    26. // 第一次GC
    27. System.gc();
    28.  
    29. Thread.sleep(1000);
    30. if(phantomObject == null){
    31. System.out.println("phantomObject is null");
    32. }//if
    33. else{
    34. System.out.println("phantomObject 可用");
    35. }
    36.  
    37. System.out.println("第二次GC");
    38. System.gc();
    39.  
    40. Thread.sleep(1000);
    41. if(phantomObject == null){
    42. System.out.println("phantomObject is null");
    43. }//if
    44. else{
    45. System.out.println("phantomObject 可用");
    46. }
    47.  
    48. System.out.println("第三次GC");
    49. System.gc();
    50.  
    51. Thread.sleep(1000);
    52. if(phantomObject == null){
    53. System.out.println("phantomObject is null");
    54. }//if
    55. else{
    56. System.out.println("phantomObject 可用");
    57. }
    58. }
    59. }
在上面代码中,先将对象phantomObject设置为null,告知GC这是一个需要清理的对象。然后,进行一次显示的GC,GC过后发现,虽然该对象的finalize()方法被调用,但是对象依然存在。随后,进行第二次GC,由于在GC之前没有清除对象的强引用,所以phantomObject依然没有被回收。

**运行结果:**

 

MyObject is finalize called

phantomObject 可用

第二次GC

phantomObject 可用

第三次GC

phantomObject 可用

 

可见,虽然finalize()被调用,但是phantomObject始终都没有被回收。如果要强制回收phantomObject,需要在第二次G前,使用phantomObject=null,去除该对象的强引用。由于finalize()只会被调用一次,因此在第二次回收时,对象就没有机会复活了。

**运行结果:**

 

MyObject is finalize called

phantomObject 可用

第二次GC

phantomObject is null

第三次GC

phantomObject is null


由于在finalize()中存在让回收对象复活的可能性,因此,在一个复杂的应用系统中,一旦finalize()方法实现有问题,就很容易造成内存泄露。而使用虚引用来清理相关资源则不会有类似的问题,因为虚引用队列中对象,事实上已经完成了对象的回收工作,是不可能再度复活该对象的。

http://www.bkjia.com/Javabc/1117555.htmlwww.bkjia.comtruehttp://www.bkjia.com/Javabc/1117555.htmlTechArticle\[Java开发之路\](25)引用类型 1. 强援引Java中的援用,类似于C++的指针。通过征引,能够对堆中的对象开始展览操作。在某些函数中,当创立了…

强引用

强引用正是指在程序代码之中布满存在的,类似 Object obj = new Object()
那类的援引,只要强援引还留存,垃圾收罗器永恒不会回收掉被引述的靶子。

软引用

用来说述一些还应该有用,但不要必需的靶子。对于软援用关联着的对象,在系统就要爆发内部存款和储蓄器溢出格外以前,将会把那个指标列进回收范围里边并张开第一遍回收。倘若这一次回收照旧没有丰盛的内存,才会抛出内部存款和储蓄器溢出极其。

在JDK 1.2后头,提供了SoftReference类来促成软引用。

弱引用

弱援用也是用来描述非必需对象的,但是它的强度比软援引更弱一些,被弱援用关联的对象只好生活到下次垃圾采摘发出从前。当垃圾收集器工作时,无论当前内部存款和储蓄器是不是丰盛,都会回收掉只被弱援引关联的目的。

在JDK 1.2随后,提供了WeakReference类来落到实处弱引用。

虚引用

虚引用也叫做幽灵援引可能幻影援引,它是最弱的一种援引关系。三个指标是还是不是有虚援引的存在,完全不会对其生存时间构成影响,也心余力绌透过虚引用来赢得三个对象实例。为三个对象设置虚引用关联的头一无二指标正是愿意能在那些目的被搜集器回收时接到贰个连串通报。

在JDK 1.2过后,提供了PhantomReference类来促成虚援用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图