Java虚拟机--判断可触及性(七)

来源:互联网 发布:强制锁定4g网络的软件 编辑:程序博客网 时间:2024/06/16 12:39

知识点的梳理:

  • 可触及性包含3种状态:对象只有在不可触及状态时才会被回收!;
    • 可触及的:从根节点开始,可以到达这个对象;
    • 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()函数中复活;
    • 不可触及的:对象的finalize()函数被调用,并且没有复活,就会进入不可触及状态。该状态的对象不可能被复活,因为finalize()函数只会被调用一次;
  • Java提供4个级别的引用:强引用,软引用,弱引用,虚引用;

     

  • 说明
    • 可触及性的必要性:不被使用的对象,很有可能借助finalize()函数复活自己,所以我们需要一个状态来判定一个对象到底是否可回收!
    • 示例1:对象的复活

public classCanReliveObj {

public staticCanReliveObjobj;

  

@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 {

obj =new CanReliveObj();

//第一次清除对象,对象通过finalize()复活

obj =null;

System.gc();

Thread.sleep(1000);

if(obj ==null){

System.out.println("objnull");

}else{

System.out.println("obj可用");

}

System.out.println("2GC");

//第二次清除对象,无法再复活

obj =null;

System.gc();

Thread.sleep(1000);

if(obj ==null){

System.out.println("objnull");

}else{

System.out.println("obj可用");

}

}

}

  • 引用和可触及性的强度
    • 软引用,弱引用,虚引用可在java.lang.ref包中找到它们。FinalReference表示"最终"引用,它用于实现对象的finalize()方法
    • 强引用
      • 能力:程序中一般的引用类型,此级别的对象是可触及的,不会被回收;
        • 能力特点:
          • 可直接访问目标对象;
          • 强引用指向的对象在任何时候都不会被回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指向的对象;
          • 强引用可能导致内存泄漏;
      • 示例:

步骤1 : StringBuffer str = new StringBuffer("Hello World");

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

步骤2:StringBuilder str1 = str;

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

这两个引用都是强引用

  • 软引用--- 可被回收的引用
    • 能力:比强引用弱一点的,软可触及的引用类型;
      • 能力特点:
        • 堆空间不足时,会被回收;
        • 使用java.lang.SoftReference类实现;
    • 示例1:软引用在系统堆内存不足时被回收

public class SoftRef {

//声明User

public static class User {

public User(intid,String name){

this.id =id;

this.name =name;

}

public intid;

public Stringname;

@Override

public String toString() {

return"User [id=" + id + ", name=" +name + "]";

}

}

public static void main(String[]args) {

Useru = new User(1,"geym");//建立user实例,强引用

SoftReference<User>userSoftRef = new SoftReference<SoftRef.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];//分配一块大内存,让系统认为内存紧张

System.gc();//垃圾回收

System.out.println(userSoftRef.get());//从软引用获取数据

}

}

运行参数:-Xmx10m
运行效果:

User [id=1, name=geym](第一次从软引用中获取数据)

After GC:

User [id=1, name=geym](GC没有清除软引用)
null(由于内存紧张,软引用被清除)

  • 示例2:每个软引用都可以附带一个引用队列,当对象的可达性状态发生改变时(由可达变为不可达),软引用对象就会进入引用队列。通过该队列,可跟踪对象的回收情况

public class SoftRefQ {

public static class User{

public User(intid,String name){

this.id =id;

this.name =name;

}

public intid;

public Stringname;

@Override

public String toString() {

return"User [id=" + id + ", name=" +name + "]";

}

}

static ReferenceQueue<User>softQueue = null;

public static class CheckRefQueueextends Thread{

@Override

public void run() {

//跟踪引用队列

while(true){

if(softQueue !=null){

UserSoftReferenceobj = null;

try{

obj = (UserSoftReference)softQueue.remove();

}catch(Exceptione){

e.printStackTrace();

}

if(obj !=null){

System.out.println("user id" +obj.uid+" is delete");

}

}

}

}

}

//实现自定义软引用类,扩展软引用的目的是记录User.uid,在后续引用队列中,可以通过这个uid知道哪个User实例被回收了

public static class UserSoftReferenceextends SoftReference<User>{

intuid;

public UserSoftReference(Userreferent,ReferenceQueue<? super User> q) {

super(referent,q);

uid=referent.id;

}

}

public static void main(String[]args) throws InterruptedException {

Threadt = new CheckRefQueue();

t.setDaemon(true);

t.start();

Useru = new User(1,"geym");//建立user实例,强引用

softQueue =new ReferenceQueue<User>();//通过强引用,建立软引用

//创建软引用时,指定了一个软引用队列,当给定的对象实例被回收时,就会被加入这个引用队列,通过访问该队列可以跟踪对象的回收情况

UserSoftReferenceuserSoftRef = new UserSoftReference(u,softQueue);

u =null;//去除强引用

System.out.println(userSoftRef.get());//从软引用获取强引用对象

System.gc();//垃圾回收

//内存足够,不会被回收

System.out.println("After GC:");

System.out.println(userSoftRef.get());//再次获取软引用中的对象

System.out.println("try to create byte array and GC");

byte[]b =new byte[1024*925*7];//分配一块大内存,让系统认为内存紧张

System.gc();//垃圾回收

System.out.println(userSoftRef.get());//从软引用获取数据

Thread.sleep(1000);

}

}

使用如下参数执行此段代码:-Xmx10m
运行结果:

User [id=1, name=geym](第一次从软引用获得对象)

After GC:

User [id=1, name=geym](GC后,软引用对象没有回收)

try to create byte array and GC(创建大数组,耗尽内存)

user id1 is delete(引用队列探测到对象被删除)

null(对象已被回收,无法再通过软引用获取对象)

  • 弱引用---发现即回收
    • 能力:弱可触及的引用类型。比软引用稍弱。只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。
      • 能力特点:
        • 垃圾回收器线程优先级很低,因此,并不一定能很快发现持有弱引用的对象。这种情况下,弱引用对象可以存在较长的时间;
        • 一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册的引用队列中;(这一点和软引用一样,可查看软引用的代码示例)
        • 使用java.lang.ref.WeakReference类实现
      • 能力分析:
        • 软引用,弱引用都适合来保存那些可有可无的缓存数据。这样做,可以让系统内存不足时,让这些缓存数据被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用;
    • 示例1:

public class WeakRef {

public static class User{

public User(intid,String name){

this.id =id;

this.name =name;

}

public intid;

public Stringname;

@Override

public String toString() {

return"User [id=" + id + ", name=" +name + "]";

}

}

public static void main(String[]args) {

Useru = new User(1,"hey");

//构造弱引用

WeakReference<User>userWeakRef = new WeakReference<User>(u);

u =null;//去除强引用

System.out.println(userWeakRef.get());//从弱引用中重新获取对象

System.gc();//GC

//不管当前内存空间足够与否,都会回收它的内存

System.out.println("After GC:");

System.out.println(userWeakRef.get());//重新尝试从弱引用中获取对象

}

}

运行结果:

User [id=1, name=hey]

After GC:

null

  • 虚引用--- 对象回收跟踪
    • 能力:虚可触及的引用了类型。所有应用类型中最弱的一个。持有虚引用的对象,和没有引用几乎是一样的。随时都可能被垃圾回收器回收。
      • 能力特点:
        • 试图通过虚引用的get()方法取得强引用时,总是会失败;
        • 虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程;
        • 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,已通知应用程序对象的回收情况;
    • 示例1:

public class TraceCanReliveObj {

public static TraceCanReliveObjobj;

static ReferenceQueue<TraceCanReliveObj>phantomQueue = null;

public static class CheckRefQueueextends Thread{

@Override

public void run() {

//跟踪引用队列

while(true){

if(phantomQueue !=null){

PhantomReference<TraceCanReliveObj>objt = null;

try{

objt = (PhantomReference<TraceCanReliveObj>)phantomQueue.remove();

}catch(Exceptione){

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 {

Threadt = new CheckRefQueue();

t.setDaemon(true);

t.start();

phantomQueue =new ReferenceQueue<TraceCanReliveObj>();

obj =new TraceCanReliveObj();

PhantomReference<TraceCanReliveObj>phantomRef = new PhantomReference<TraceCanReliveObj>(obj,phantomQueue);

obj =null;

System.gc();

Thread.sleep(1000);

if(obj==null){

System.out.println("objnull");

}else{

System.out.println("obj可用");

}

System.out.println("2GC");

obj =null;

System.gc();

Thread.sleep(1000);

if(obj ==null){

System.out.println("objnull");

}else{

System.out.println("obj可用");

}

}

}

执行效果:

CanReliveObj finalize called(对象复活)

obj可用

第2次GC(第2次,对象无法复活)

TraceCanReliveObj is delete by GC(引用队列捕获到对象被回收)

obj是null

0 0