引用类型与垃圾回收

引用是与垃圾回收机制相关的 , 从JDK1.2开始 , 把引用划分为4种级别

从而使程序能够更加灵活地控制对象的生命周期

级别从高到低分别是

强引用 —> 软引用 —> 弱引用 —> 虚引用

↑ 强引用是作为基类的  , 另外三种引用类型都是它的子类

(一) 强引用 ( StrongReference )

强引用是最普遍的引用 , 如果一个对象具有强引用 , 那么垃圾回收器绝不会回收它

如果具备强引用的对象过多 , Java虚拟机内存不足 , 就会抛出 OutOfMemoryError , 程序终止

(二) 软引用 ( SoftReference )

如果一个对象只具有软引用 , 当Java虚拟机内存空间足够时 , 垃圾回收器就不会回收它

如果内存空间不足 , 这个对象就会被垃圾回收器回收掉

软引用可以用来实现内存敏感的高速缓存

也可以与一个引用队列 ( ReferenceQueue ) 联合使用 , 如果软引用所引用的对象被垃圾回收器回收

Java虚拟机会把这个软引用加入到与之关联的引用队列当中

(三) 弱引用 ( WeakReference )

弱引用的对象拥有更短暂的生命周期 , 在垃圾回收器扫描它所管辖的内存区域的过程中

一旦发现只具有弱引用的对象 , 不论内存空间是否足够 , 都会将其回收

但是垃圾回收器是一个优先级很低的线程 , 因此不一定会很快发现那些只具有弱引用的对象

弱引用同样可以与引用队列联合使用

(四) 虚引用 ( PhantomReference )

顾名思义 , 就是形同虚设 , 一个对象只持有虚引用 , 就与没有任何引用一样 , 随时会被垃圾回收器回收掉

与弱引用的区别是 , 虚引用必须要与引用队列联合使用



实践

--> 创建一个对象的弱引用

public class Mian<T extends Exception> {
     public static void main(final String[] args){
          Demo demo = new Demo();
           //创建一个弱引用
          WeakReference<Demo> weakRef = new WeakReference<Demo>(demo);
           //去掉该对象的强引用(此时它只具备一个弱引用)
          demo = null;
           //运行垃圾回收器
          System.gc();
           /*如果不请求执行垃圾回收,则不能保证在这个时候弱引用对象被回收*/

           //重新获得该对象的强引用
          demo = weakRef.get();
           if(demo == null ){
               //如果该对象已经被垃圾回收器回收掉,那么获取到的就是null
              System.out.println("The target is null" );
          } else {
              System.out.println(demo.num );
          }
     }
}

class Demo {
     int num = 10;
}

执行的结果是当垃圾回收器启动的时候 , 弱引用对象已经被回收

无法再次获取到强引用 ( get方法的返回值是null )

软引用和虚引用的创建方式与此也是类似的

在实际的程序设计当中 , 除了一般的强引用之外 , 软引用使用得相对较多

因为软引用可以加速JVM对垃圾内存的回收速度 , 维护系统的运行安全 , 防止产生内存溢出的问题


附 : 使用软引用集合创建一个高速缓存器

package com.system.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;

import org.springframework.stereotype.Repository;
/**
 * 数据库数据的高速缓存器
 * 使用软引用构成的集合实现
 * 在根据执行查询的时候,就把查询到的对象放入到缓存当中
 * 当该数据发生改变(被修改或删除),就从集合中移除该对象
 * 由于内存的限制,当集合中的数据过多时
 * 软引用对象会被垃圾回收器回收,避免内存溢出的情况
 * @author 41882
 *
 */
@Repository
public class DataCache {
    private Hashtable<String,DataRef> dataRefs;//缓存区
    private ReferenceQueue<Object> queue;//引用队列

    public DataCache() {
        dataRefs = new Hashtable<String,DataRef>();
        queue = new ReferenceQueue<Object>();
    }
    /**
     * 用于创建实例对象软引用的类
     * @author 41882
     *
     */
    private class DataRef extends SoftReference<Object> {
        public DataRef(Object obj,ReferenceQueue<Object> queue){
            super(obj,queue);
            String id = (String) ReflectUtils.getItemField(obj, "id");
            //缓存中的标识是该类名称与ID的组合
            this._key = obj.getClass().getSimpleName()+id;
        }
        private String _key;
    }
    /**
     * 从缓存区获取一个对象(如果缓存区没有该对象,则执行查询获得该对象)
     * @param <T>
     * @param id
     * @param clz
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getObject(Class<T> clz,String id){
        //从缓存中获取该实例的软引用
        DataRef ref = dataRefs.get(clz.getSimpleName() + id);
        if(ref == null){
            return null;
        } else {
            //由软引用获取强引用
            //如果该软引用对象已被回收,返回null
            return (T) ref.get();
        }
    }
    /**
     * 从缓存区当中移除一个对象
     * (通常在该对象被修改或删除的时候,就从缓存区移除该对象)
     * @param obj
     */
    public void removeObject(Object obj){
        String id = (String) ReflectUtils.getItemField(obj, "id");
        dataRefs.remove(obj.getClass().getSimpleName() + id);
    }
    /**
     * 从缓存区当中根据ID和类型移除多个对象
     * @param clz
     * @param ids
     */
    public void removeObject(Class<?> clz,String[] ids){
        if(ids != null && ids.length>0){
            for(String id : ids){
                dataRefs.remove(clz.getSimpleName() + id);
            }
        }
    }
    /**
     * 清空缓存区
     */
    public void clearCache(){
        dataRefs.clear();
    }
    /**
     * 缓存数据
     * @param obj 需要执行缓存的对象
     */
    public void cacheData(Object obj) {
        cleanQueue();
        DataRef ref = new DataRef(obj,queue);
        dataRefs.put(obj.getClass().getSimpleName() + ReflectUtils.getItemField(obj, "id"), ref);
    }

    /**
     * 清除已经被回收的软引用对象
     */
    private void cleanQueue(){
        DataRef ref = null;
        while((ref=(DataRef) queue.poll()) != null) {
            dataRefs.remove(ref._key);
        }
    }
}
时间: 2024-10-25 11:07:13

引用类型与垃圾回收的相关文章

Java深度历险(四)——Java垃圾回收机制与引用类型

Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对开发人员提出了比较高的要求,容易造成内存访问错误和内存泄露等问题.一个常见的问题是会产生“悬挂引用(dangling references)”,即一个对象引用所指向的内存区块已经被错误的回收并重新分配给新的对象了,程序如果继续使用这个引用的话会造成不可预期的结果.开发人员有可能忘记显式的调用释放内存

数往知来C#之接口 值类型与引用类型 静态非静态 异常处理 GC垃圾回收 值类型引用类型内存分配&lt;四&gt;

C# 基础接口篇 一.多态复习 使用个new来实现,使用virtual与override    -->new隐藏父类方法 根据当前类型,电泳对应的方法(成员)    -->override重写 无论什么情况,都是执行新的方法(成员) 继承是实现多态的一个前提,没有继承多态是不能实现的 父类与子类实现多态 抽象类与子类实现 抽象类不能实例化 抽象类中的抽象方法没有方法体 抽象类的成员有哪些   ->包含非抽象成员   ->不能实例化   ->子类必须实现父类的 抽象方法,除非子

第三章 JVM内存回收区域+对象存活的判断+引用类型+垃圾回收线程

注意:本文主要参考自<深入理解Java虚拟机(第二版)> 说明:查看本文之前,推荐先知道JVM内存结构,见<第一章 JVM内存结构> 1.内存回收的区域 堆:这是GC的主要区域 方法区:回收两样东西 无用的类 废弃的常量 栈和PC寄存器是线程私有区域,不发生GC 2.怎样判断对象是否存活 垃圾回收:回收掉死亡对象所占的内存.判断对象是否死亡,有两种方式: 引用计数法 原理:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值+1:引用失效时,计数器值-1 实际中不用,不用的两

枚举|标志枚举+|(或)和&amp;(与)运算|类型转换|值类型和引用类型|传参|异常|垃圾回收

枚举部分 enum 关键字用于声明枚举,即一种由一组称为枚举数列表的命名常量组成的独特类型. 通常情况下,最好是在命名空间内直接定义枚举,以便该命名空间中的所有类都能够同样方便地访问它. 但是,还可以将枚举嵌套在类或结构中.默认情况下,第一个枚举数的值为 0,后面每个枚举数的值依次递增 1. 例1: //此枚举的默认值是从0开始 enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri}; 例2: //此枚举的默认值是从1开始,下标为3的tue值为7,从下标3开始

java 垃圾回收机制 引用类型

Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对开发人员提出了比较高的要求,容易造成内存访问错误和内存泄露等问题.一个常见的问题是会产生“悬挂引用(dangling references)”,即一个对象引用所指向的内存区块已经被错误的回收并重新分配给新的对象了,程序如果继续使用这个引用的话会造成不可预期的结果.开发人员有可能忘记显式的调用释放内存

.NET内存管理、垃圾回收

1. Stack和Heap    每个线程对应一个stack,线程创建的时候CLR为其创建这个stack,stack主要作用是记录函数的执行情况.值类型变量(函数的参数.局部变量 等非成员变量)都分配在stack中,引用类型的对象分配在heap中,在stack中保存heap对象的引用指针.GC只负责heap对象的释 放,heap内存空间管理 Heap内存分配        除去pinned object等影响,heap中的内存分配很简单,一个指针记录heap中分配的起始地址,根据对象大小连续的分

垃圾回收机制GC知识再总结兼谈如何用好GC(其他信息: 内存不足)

来源 一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般有如下几种: 1.手动管理:C,C++ 2.计数管理:COM 3.自动管理:.NET,Java,PHP,GO- 但是,手动管理和计数管理的复杂性很容易产生以下典型问题: 1.程序员忘记去释放内存 2.应用程序访问已经释放的内存 产生的后果很严重,常见的如内存泄露.数据内容乱码,而且大部分时候,

深入理解JVM之垃圾回收详解

一.垃圾收集的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的一个系统级线程会自动释放该内存块.垃圾收集意味着程序不再需要的对象是"无用信息",这些信息将被丢弃.当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用.事实上,除了释放没用的对象,垃圾收集也可以清除内存记录碎片.由于创建对象和垃圾收集器释放丢弃对象所占的内存空间,内

Java虚拟机5:Java垃圾回收(GC)机制详解

http://www.cnblogs.com/xrq730/p/4836700.html 哪些内存需要回收? 哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无非就是那些不可能再被任何途径使用的对象.那么如何找到这些对象? 1.引用计数法 这个算法 的实现是,给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1:当引用失效时,计数器值-1.任何时刻计数值为0的对象就是不可能再 被使用的.这种算法使用场景很多,但是,Java中却没有使用这种算法,因为这种算法很