java四种依赖类型及应用场景

您所在的位置:网站首页 依赖关系有哪几种类型 java四种依赖类型及应用场景

java四种依赖类型及应用场景

2024-07-12 19:53| 来源: 网络整理| 查看: 265

说到依赖类型,可能很多人在实际开发场景中并没有真正的关心过,但是如果能够理解并熟练的使用java的依赖类型和级别,能够给我们的开发以及程序性能带来不小的性能提升。

开门见山地说,java提供了四种依赖类型,根据依赖的从强到弱分为:强依赖、软依赖、弱依赖和虚依赖(也叫幽灵依赖)。今天我们就逐个分析一下四种依赖类型以及其应用场景。

强依赖

强依赖是我们日常开发中使用最频繁的依赖类型,也是最简单易用的类型,可以这么说,如果你没有关注和了解过依赖级别,那么你所写的代码都是强依赖;比如最常见的:

TestDemo demo = new TestDemo();

TestDemo demo = TestDemo.class.newInstance();

我们最常用的直接new一个对象或者通过反射创建一个对象,都是强依赖;对于强依赖,有一个很关键的点,就是如果jvm内存不足的时候,就算要报om异常也不会回收这一部分内存。

通过ide把jvm启动内存调到足够小,然后不停的创建强引用类型依赖:

图中把应用jvm启动内存调小到1m,然后运行一下代码:

public static void main(String[] args) {

String str = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";

List list = new ArrayList();

for(int i = 0;i < 100000;i ++) {

list.add(i + str);

}

System.out.println(list.size());

}

测试代码中使用列表的形式不停的创建强依赖,运行后结果如下图:

不出意外地,我们看到了内存溢出om错误,也就是说在程序运行过程中,jvm并没有因为内存使用过多而回收强引用对象,也就验证了我们对强引用类型的分析,并且在实际开发场景中,由于程序bug或者代码不规范真的会出现过分的创建强引用对象导致系统内存溢出,更重要的一点内存溢出是错误不是异常,错误和异常的最大区别就是错误会直接导致jvm终止运行,风险系数特别高,所以在开发中一定要做代码评审来最大程度避免om。

软依赖

软依赖,从名字上也可以看出,依赖强度没有强依赖大;而软依赖和强依赖的最大区别是,在jvm报om错误之前会回收软引用对象,也就是系统崩溃前的最后一刻殊死一搏。软引用的典型应用场景就是高速缓存(jvm缓存),高速缓存与redis以及其他缓存框架的最大优势是,没有网络开销和不存在数据一致性,其实就是存放在jvm内存的缓存数据。以查询用户信息为例,程序优先去jvm缓存取数据,如果没有取到就去DB查询,如下图:

然后运行一下测试代码:

public User selectById(Long id) {

User u = new User();

SoftReference studentSoftRef = new SoftReference(u);

u = null;

System.out.println(studentSoftRef.get());

System.gc();

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

System.out.println(studentSoftRef.get());

System.out.println("After a big object allocate:");

byte[] b = new byte[1024 * 925 * 7];//分配一个大对象

System.gc();

System.out.println(studentSoftRef.get());//由于内存紧张,软引用会被清除

return null;

}

运行结果如下:

很明显可以看到,在程序创建一个新的大对象之后垃圾回收线程把软引用持有的对象回收掉了。刚开始创建一个对象并且创建了软引用,然后有个关键点u = null释放强引用,然后创建一个大对象后调用System.gc()告诉垃圾回收线程可以执行回收了(但是不一定立即执行),此处发现jvm堆内存不够用,为了避免jvm报om错误退出,执行了垃圾回收释放掉了软引用持有的对象。最后我们看到软引用已经不持有对象了。

弱依赖

弱引用相对软引用,依赖强度又弱了一些;而弱引用和软引用的区别是,每次垃圾回收都会回收掉弱引用持有的对象。弱引用典型的应用场景ThreadLocal,之前有一篇文章讲解过ThreadLocal实现线程隔离的原理,本质上是线程级别的存储,也就是说每次gc回收都会把ThreadLocal持有的对象回收掉。如下图模拟两个请求调用同一个线程获取值(两次调用之间有垃圾回收):

接着运行测试代码:

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

User u = new User();

WeakReference studentWeakRef = new WeakReference(u);

u = null;

System.out.println(studentWeakRef.get());

System.gc();

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

System.out.println(studentWeakRef.get());//gc之后一定会被回收

}

运行结果如下图:

可以看到手动触发一次gc后,弱引用持有的对象被回收了,也就说明了我们之前的论述--每次垃圾回收都会回收弱引用持有的对象内存(前提是不能同时被强引用持有)。

虚依赖

虚引用也叫作幽灵引用,日常业务开发中用的很少,和软引用及弱引用的区别是,虚引用并不会决定对象的生命周期,并且虚引用一定要引用队列(ReferenceQueue)一起使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。

使用场景是,对象销毁前的一些操作,比如说资源释放等。Object.finalize()虽然也可以做这类动作,但是这个方式即不安全又低效。

总结

上述详细描述了java四种依赖类型以及其应用场景,个人觉得软引用对我们最实用,在写一些高速缓存时,如果巧妙地使用软引用能够一定程度上解决系统的om错误退出。希望经过这一篇的赘述能够帮助各位看官对引用类型的理解,也希望在实际开发过程中能够给应用带来立竿见影的优化。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3