Java中的引用和动态代理的实现详解

这篇文章主要介绍了Java中的引用和动态代理的实现详解,涉及Java中的引用类型,JVMGC的可达性分析,代理模式等相关内容,具有一定参考价值,需要的朋友可以了解下。

我们知道,动态代理(这里指JDK的动态代理)与静态代理的区别在于,其真实的代理类是动态生成的。但具体是怎么生成,生成的代理类包含了哪些内容,以什么形式存在,它为什么一定要以接口为基础?

如果去看动态代理的源代码(java.lang.reflect.Proxy),会发现其原理很简单(真正二进制类文件的生成是在本地方法中完成,源代码中没有),但其中用到了一个缓冲类java.lang.reflect.WeakCache[],Class>,这个类用到了弱引用来构建。

在JDK的3个特殊引用中,弱引用是使用范围最广的,它的特性也最清晰,相对而言,其他两种逻辑稍显晦涩,源码中的注释也语焉不详。本文将简单介绍几种引用的行为特征,然后分析一下弱引用的一些实际应用场景,其中包含了动态代理中的实现。本文将包含以下内容:

JDK中的引用类型

不同引用类型对GC行为的影响

引用类型的实现

ThreadLocal对弱引用的使用

动态代理对弱引用的实现

虚引用如何导致内存泄漏

JDK中「引用(Reference)」的类型

Java的所有运行逻辑都是基于引用的,其形态类似于不可变的指针,所以在Java中会有一些很绕的概念,比如说,Java中函数的传参是值传递,但这里所说的值,其实是引用的值,所以你可以通过一个函数的参数来修改其对象的值。另一方面,Java中还存在一些基本数据类型,它们没有引用,传递的是真实的值,所以不能在函数内部修改参数的值。

关于引用,Java中有这样几种:

1.强引用

所有对象的引用默认为强引用,普通代码中,赋值语句之间传递的都是强引用,如果一个对象可以被某个线程(活着的,下同)通过强引用访问到,则称之为强可达的(StronglyReachable)。

强可达的对象不会被GC回收。

2.软引用(SoftReference

当一个对象不是强可达的,但可以被某个线程通过软引用访问到,则称之为软可达的(SoftlyReachable)。

软可达的对象的引用只有在内存不足时会被清除,使之可以被GC回收。在这一点上,JVM是不保证具体什么时候清除软引用,但可以保证在OOM之前会清除软可达的对象。同时,JVM也不保证软可达的对象的回收顺序,但OracleJDK的文档中提到,最近创建和最近使用的软可达对象往往会最后被回收,与LRU类似。

关于软可达的对象何时被回收,可以参考Oracle的文档。

3.弱引用(WeakReference

当一个对象不是强可达的,也不是软可达的,但可以被某个线程通过弱引用访问到,则称之为弱可达的(WeaklyReachable)。

弱引用是除了强引用之外,使用最广泛的引用类型,它的特性也更简单,当一个对象是弱可达时,JVM就会清除这个对象上的弱引用,随后对这个对象进行回收动作。

4.虚引用(PhantomReference

当一个对象,通过以上几种可达性分析都不可达,且已经finalized,但有虚引用指向它,则它是虚可达的(PhantomReachable)。

虚引用是行为最诡异,使用方法最难的引用,后边会讲到。

虚引用不会影响GC,它的get()方法永远返回null,唯一的用处就是在GCfinalize一个对象之后,它会被放到指定的队列中去。关于这个队列会在下边说明。

不同GC行为背后的原理

JVMGC的可达性分析

JVM的GC通过可达性分析来判断一个对象是否可被回收,其基本思路就是以GCRoots为起点遍历链路上所有对象,当一个对象和GCRoots之间没有任何的直接或间接引用相连时,就称之为不可达对象,则证明此对象是不可用的。

而进一步,Java中又定义了如上所述的4种不同的可达性,用来实现更精细的GC策略。

Finalaze和ReferenceQueue

对于普通的强引用对象,如果其变成不可达之后,通常GC会进行Finalize(Finalize主要目的是让用户可以自定义释放资源的过程,通常是释放本地方法中使用的资源),然后将它的对象销毁回收,但对于本文中讨论的3种引用,还有可能在这个过程中做一些别的事情:

GC根据约定的规则来决定是否清除这些引用

这方面上一节已经讲过了,每个引用类型都有约定的处理规则。

如果它们注册了引用队列,在Finalize对象后,将引用的对象放入队列。

主要用来使开发者可以得到对象被销毁的通知,当然,如虚引用这样的,其引用不会自动被清除,所以它可以阻止其所引用的对象被回收。

引用(java.lang.ref.Reference)对象的状态

这里所说的「引用对象」指的是由类java.lang.ref.Reference生产的对象,这个对象上保持了「需要特殊处理的」对「目标对象」的引用。

引用对象有4种状态,根据它与其注册的队列的关系,分为以下4种:

Active

引用对象的初始状态,表示GC要按照特殊的逻辑来处理这个对象,大致方法就是按照上一节提到的。

Pending

如果一个引用对象,其注册了队列,在入队之前,会进入这个状态。

Enqueued

一个引用对象入队后,进入这个状态。

Inactive

一个引用对象出队后,或者没有注册队列,其队列是一个特殊的对象java.lang.ref.ReferenceQueue.NULL,表示这个对象已经没有用了。

几种引用的实际应用

日常开发工作中,用到除强引用之外的引用的可能性很小,只有在处理一些性能敏感的逻辑时,才需要考虑使用这些特殊的引用,下面就举几个相关的实际例子,分析其使用场景。

软引用

弱引用的使用比较简单,如Guava中的LocalCache中就是用了SoftReference来做缓存。

弱引用

弱引用是使用的比较多的,从上文的描述可知:对于一个「目标对象A」,如果还有强引用指向它,那么从一个弱引用就可以访问到A,一旦没有强引用指向它,那么就可以认为,从这个弱引用就访问不到A了(实际情况可能会有偏差)。

根据这个特点,JDK中注释说到,弱引用通常用来做映射表(canonicalizingmapping),总结下来映射表有这样2个特点:

如果表中的Key(或者Value)还存在强引用,则可以通过Key访问到Value,反之则访问不到

换句话说,只要有原始的Key,就能访问到Value。

映射表本身不会影响其中Key或者Value的GC

在JDK中有很多个地方使用了它的这个特点,下面是2个具有代表性的实例。

1.ThreadLocal

ThreadLocal的原理比较简单,线程中保持了一个以ThreadLocal为Key的ThreadLocal.ThreadLocalMap对象threadLocals,其中的Entry如代码1中所示:

 //代码1 static class Entry extends WeakReference

其引用关系如下图所示:

ThreadLocal中的引用关系

从上图可以看出,当引用2被清除之后(ThreadLocal对象不再使用),如果引用4为强引用,则不论引用1是否还存在,只要Thread对象还没死,则对象1和对象2永远不会被释放。

2.动态代理

动态代理是Java世界一个十分重要的特性,对于需要做AOP的业务逻辑十分重要。JDK本身提供了基于反射的动态代理机制,其原理大致是要通过预先定义的接口(interface)来动态的生成代理类,并将之代理到InvocationHandler的实例上去。JDK的动态代理使用起来很简单,如下代码2中所示:

 //代码2 package me.lk; import java.lang.reflect.*; public class TestProxy { /** * 两个预定义的需要被代理的接口 */ public static interface ProxiedInterface { void proxiedMethod(); } public static interface ProxiedInterface2 { void proxiedMethod2(); } /** * 真正的处理逻辑 */ public static class InvoHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("in proxy:" + method.getName()); //其他逻辑 System.out.println("in proxy end"); return null; } } public static void main(String[] args) { InvoHandler ih = new InvoHandler(); ProxiedInterface proxy = (ProxiedInterface) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), new Class[]{ProxiedInterface.class, ProxiedInterface2.class}, ih); proxy.proxiedMethod(); ProxiedInterface2 p = (ProxiedInte

以上就是Java中的引用和动态代理的实现详解的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java