对象引用分析

本文介绍了Java中对象的三种状态及其与垃圾回收的关系,详细解释了引用计数法和可达性分析算法的工作原理,同时深入探讨了Java中四种引用类型的特性和应用场景。

对象的三种状态:

可达的

从根节点可以触及到这个对象

可复活的

一旦所有引用被释放,就是可复活状态

因为在finalize()中可能复活该对象

不可达的

在finalize()后,可能会进入不可触及状态

不可达的对象不可能复活

可以回收

 

引用标记

Java虚拟机提供自动内存管理机制。在GC中,没用的对象,内存是要回收的。如何高效判断对象存活是个重要的问题。

引用计数法

此算法计算对象被引用的次数。引用失效就减一。当对象引用个数为0时表示不被引用。引用计数法,失效简单,效率高是个不错的算法。但Java虚拟机并不使用引用计数器算法作为对象的引用标记。原因是不能处理对象互相引用问题。如A↔B,两个对象互相引用。AB对象缺没有被引用,形成引用孤岛。

 

可达性分析算法

可达性分析算法,是通过一系列被称为"GC Roots"的对象作为起点,从起点开始的引用链都没有被链接,表示对象是不可达的。

Java中GC Roots对象:

  1. 虚拟机栈中引用的对象

  2. 方法区类静态属性引用的对象

  3. 方法区常量引用的对象

  4. 本地方法栈引用的对象

Minor GC

Minor GC,垃圾收集器不需要扫描整个老年代就能识别新生代中的存活对象。HotSpot VM垃圾收集器使用卡表(Card Table)作为"GC Roots"。卡表是单字节数组,每个数组元素对应堆中的一张卡。老年代以512字节为块划分为若干张卡,每次老年代对象中某个引用新生代的字段发生变化是会更新对应卡表的值,并标记卡脏卡,垃圾收集器只会在脏卡中查找老年代-新生代引用。

卡表中每一个位表示年老代4K的空间,卡表记录为0的老年代区域没有任何对象指向新生代,只有卡表位为1的区域才有对象包含新生代引用,因此在新生代GC时,只需要扫描卡表位为1所在的年老代空间。使用这种方式,可以大大加快新生代的回收速度。

Hotspot VM的字节码解释器和JIT编译器使用写屏障维护卡表。写屏障是一小段将卡状态设置为脏的代码。解释器每次执行更新引用的字节码时,都会执行一段写屏障,JIT编译器在生成更新引用的代码后,也会生成一段写屏障。虽然写屏障使得应用线程增加了一些性能开销,但Minor GC变快了许多,整体的垃圾收集效率也提高了许多,通常应用的吞吐量也会有所改善。

支配树

 

 

对象的引用类型

对象分为被引用和不被引用,但这并不利于描述数据的重要级别。对于不同的场景,提供最优的对象引用策略,引用类型细分是有必要的。

  1. 对于计算过程的对象,当然在运算结束之前都不应该被垃圾回收。
  2. 对于频繁使用数据,希望不必重复读写,作为缓存。尽可能的不要垃圾回收。
  3. 对于不重要的对象,希望垃圾回收的时候直接回收,不用关心是否被引用。

Java中所有特殊引用类型的父类。引用类型不会被直接回收(会有特殊策略)。这些引用类型多被用于缓存,缓存的目的是对象有条件的垃圾回收。Reference并不会产生对象。对象被Reference包装,成为引用对象。如果引用包装的对象没被直接使用(即强引用),也不会被GC直接垃圾回收。如果引用的对象在使用,跟普通对象没有确保是不会被垃圾回收的。引用的多种策略是在对象没有被使用的情况下发生的。

 

强引用(StrongReference)

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

Object o=new Object();类型的都是强引用。此时o叫实例,而不能说o是对象。实例在栈中,对象在堆中,操作实例实际上是通过实例的指针间接操作对象。多个实例可以指向同一个对象。

强引用在ArrayList的实现源代码:

ArrayList中用于保存数据的elementData数组。在调用clear()方法实际上是让数组中每个元素指向空,并不释放内存。对于集合的多次使用,节约内存申请和释放,并且如果元素个数差别不大,也省去了数组动态扩张、数组复制的时间。

☞注:在ArrayList类中定义了一个私有的变量elementData数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。不同于elementData=null,强引用仍然存在,避免在后续调用 add()等方法添加元素时进行重新的内存分配。使用如clear()方法中释放内存的方法对数组中存放的引用类型特别适用,这样就可以及时释放内存。

 

软引用(SoftReference)

软引用多用在高效缓存,不会轻易被垃圾回收。软引用内存回收发生在GC完成后内存还不足的情况。保证回收发生在抛出OutOfMemoryError之后。一般来讲,是二次GC。如果内存充足,软引用是不会被垃圾回收的。

弱引用(WeakReference)

弱引用对象在垃圾回收时会被回收。

用弱引用实现的API

 

虚引用(PhantomReference)

"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

其它API

ReferenceQueue,一种当 weak, soft, phantom的reference被GC回收后,提供事件回调的接口。需要在实例化三大reference时,通过构造函数传入,phantom reference是强制需要传入的,weak和soft可不传。


回调过程:

GC回收reference后(phantom是在回收前,finalize后),将reference enqueue到RQ中,程序通过调用RQ的remove方法来感知reference被GC回收的事件。

 

<think>好的,我需要回答用户的问题:“对象引用是对象吗”。首先,我得理解用户的问题。用户可能在初学面向对象编程,对引用和对象的关系有疑惑。 接下来,我应该从基本概念入手。对象引用和对象本身是不同的概念。对象存储在内存的堆区,而引用是指向该内存地址的变量。这时候需要明确两者的区别。 然后,我需要举例说明。比如在Java中,声明一个String类型的引用变量,如String s = new String("hello"); 这里s是引用,而new创建的是对象。引用s存储的是对象在内存中的地址,而不是对象本身。 可能还需要讨论引用变量和基本类型变量的区别。基本类型变量直接存储值,而引用变量存储的是地址。这样用户能更清楚两者的不同。 还要考虑用户可能的误解,比如是否引用本身占内存。是的,引用变量本身占用内存空间,通常是一个指针的大小,而对象占用的内存更大,包含所有属性和方法的数据。 另外,可以提到空引用的概念,比如s = null,这时引用不指向任何对象,但引用变量本身仍然存在。这也说明引用和对象是分离的。 总结时,要强调引用和对象的区别:引用是访问对象的媒介,不是对象本身。同时,可以提醒用户注意内存管理和垃圾回收机制,当没有引用指向对象时,对象可能被回收,这进一步说明两者是独立的。 最后,检查是否有遗漏点,确保回答结构清晰,逐步引导用户理解问题,避免使用复杂术语,保持解释通俗易懂。</think>让我们逐步分析"对象引用是对象吗"这个问题: 1️⃣ **基本概念区分** - **对象**:存储在内存堆中的实体数据,包含属性和方法 - **引用**:指向对象内存地址的指针变量,存储在栈内存中 $$\text{引用变量} \rightarrow \text{内存地址} \rightarrow \text{对象实体}$$ 2️⃣ **内存结构示例** ```java // Java示例 String s = new String("Hello"); ``` - `new String("Hello")` ➔ 在堆中创建对象(占用约 24 字节) - `s` ➔ 引用变量(占用 4/8 字节,取决于 JVM 位数) 3️⃣ **关键特性对比** | 特性 | 对象 | 引用 | |-----------|--------------------|--------------------| | 存储位置 | 堆内存 | 栈内存 | | 存储内容 | 对象数据(字段、方法表等) | 内存地址指针 | | 生命周期 | 由垃圾回收机制管理 | 随作用域结束自动释放 | | 空值表现 | 不可能为空 | 可以赋值为`null` | 4️⃣ **操作验证** ```python # Python示例 a = [1,2,3] # a是引用 b = a # 复制引用 b.append(4) print(a) # 输出[1,2,3,4],验证操作的是同一个对象 ``` 5️⃣ **特殊状态说明** - **悬空引用**(Dangling Reference):当对象被回收但引用仍存在时 - **内存泄漏**:当对象存在但所有引用都丢失时(某些语言中) **结论**:对象引用不是对象本身,而是访问对象的媒介。引用相当于对象的"遥控器",存储着对象的位置信息,但本身不包含对象的实际数据。理解这个区别对内存管理和编程实践至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值