通过软引用和弱引用提升JVM内存使用性能的方法(面试时找机会说出,一定能提升成功率)
初学者或初级程序员在面试时如果能证明自己具有分析内存用量和内存调优的能力,这相当有利,因为这是针对5年左右相关经验的高级程序员的要求。而对于高级程序员来说,如果能在面试时让面试官感觉你确实做过内存调优的工作,那么面试官很有可能不问Java Core部分的其它问题了,毕竟虚拟机调优是Java Core部分非常资深的知识点。
在Java对象里,有强弱软虚四种引用,它们都和垃圾回收流程密切相关,在项目里,我们可以通过合理地使用不同类型的引用来优化代码的内存使用性能。
指向通过new得到的内存空间的引用叫强引用。比如有String a = newString(“123”);,其中的a就是一个强引用,它指向了一块内容是123的堆空间。
平时我们用的最多的引用就是强引用,以至于很多人还不知道有其他类型引用的存在,下面我们来说下弱软虚这三种平时不常见(但在关键时刻不可替代)的用途。
1 软引用和弱引用的用法
软引用(SoftReference)的含义是,如果一个对象只具有软引用,而当前虚拟机堆内存空间足够,那么垃圾回收器就不会回收它,反之就会回收这些软引用指向的对象。
弱引用(WeakReference)与软引用的区别在于,垃圾回收器一旦发现某块内存上只有弱引用(一定请注意只有弱引用,没强引用),不管当前内存空间是否足够,那么都会回收这块内存。
通过下面的ReferenceDemo.java,我们来看下软引用和弱引用的用法,并对比一下它们的差别。
1 import java.lang.ref.SoftReference;
2 import java.lang.ref.WeakReference;
3 public class ReferenceDemo {
4 public static void main(String[] args) {
5 // 强引用
6 String str=new String("abc");
7 SoftReference<String> softRef=new SoftReference<String>(str); // 软引用
8 str = null; // 去掉强引用
9 System.gc(); // 垃圾回收器进行回收
10 System.out.println(softRef.get());
11 // 强引用
12 String abc = new String("123");
13 WeakReference<String> weakRef=new WeakReference<String>(abc); // 弱引用
14 abc = null; // 去掉强引用
15 System.gc(); // 垃圾回收器进行回收
16 System.out.println(weakRef.get());
17 }
18 }
在第8行里,我们定义了SoftReference<String>类型的软引用softRef,用来指向第7行通过new创建的空间,在第14行,我 们是通过弱引用weakRef指向第13行创建的空间。
接下来我们通过下表来观察下具体针对内存空间的操作。
行号 |
针对内存的操作以及输出结果 |
6 |
在堆空间里分配一块空间(假设首地址是1000),在其中写入String类型的abc,并用str这个强引用指向这块空间。 |
7 |
用softRef这个软引用指向1000号内存,这时1000号内存上有一个强引用str,一个软引用softRef |
8 |
把1000号内存上的强引用str撤去,此时该块内容上就只有一个软引用softRef |
9 |
通过System.gc(),启动垃圾回收动作 |
10 |
通过softRef.get()输出软引用所指向的值,此时1000号内存上没有强引用,只有一个软引用。但由于此时内存空间足够,所以1000号内存上虽然只有一个软引用,但第9行的垃圾回收代码不会回收1000号的内存,所以这里输出结果是123。 |
12 |
在堆空间里分配一块空间(假设首地址是2000),在其中写入String类型的123,并用abc这个强引用指向这块空间。 |
13 |
用weakRef这个弱引用指向2000号内存,这时2000号内存上有一个强引用abc,一个软引用weakRef |
14 |
把2000号内存上的强引用abc撤去,此时该块内容上就只有一个弱引用weakRef |
15 |
通过System.gc(),启动垃圾回收动作 |
16 |
通过weakRef.get()输出软引用所指向的值,此时2000号内存上没有强引用,只有一个弱引用,所以第15行的垃圾回收代码会回收2000号的内存,所以这里输出结果是null。 |
2 软引用的使用场景
比如在一个博客管理系统里,为了提升访问性能,在用户在点击博文时,如果这篇博文没有缓存到内存中,则需要做缓存动作,这样其它用户在点击同样这篇文章时,就能直接从内存里装载,而不用走数据库,这样能降低响应时间。
我们可以通过数据库级别的缓存在做到这点,这里也可以通过软引用来实现,具体的实现步骤如下。
第一,可以通过定义Content类来封装博文的内容,其中可以包括文章ID、文章内容、作者、发表时间和引用图片等相关信息。
第二,可以定义一个类型为HashMap<String, SoftReference<Content>>的对象类保存缓存内容,其中键是String类型,表示文章ID,值是指向Content的软引用。
第三,当用户点击某个ID的文章时,根据ID到第二步定义的HashMap里去找,如果找到,而且所对应的SoftReference<Content>值内容不是null,则直接从这里拿数据并做展示动作,这样不用走数据库,可以提升性能。
第四,如果用户点击的某个文章的ID在HashMap里找不到,或者虽然找到,但对应的值内容是空,那么就从数据库去找,找到后显示这个文章,同时再把它插入到HashMap里,这里请注意,显示后需要撤销掉这个Content类型对象上的强引用,保证它上面只有一个软引用。
来分析下用软引用有什么好处?假设我们用1个G的空间缓存了10000篇文章,这10000篇文章所占的内存空间上只有软引用。如果内存空间足够,那么我们可以通过缓存来提升性能,但万一内存空间不够,我们可以依次释放这10000篇文章所占的1G内存,释放后不会影响业务流程,最多就是降低些性能。
对比一下,如果我们这里不用软应用,而是用强引用来缓存,由于不知道文章何时将被点击,我们还无法得知什么时候可以撤销这些文章对象上的强引用,或者即使我们引入了一套缓存淘汰流程,但这就是额外的工作了,这就没刚才使用“软引用“那样方便了。
3 通过WeakHashMap来了解弱引用的使用场景
WeakHashMap和HashMap很相似,可以存储键值对类型的对象,但我们可以从它的名字上看出,其中的引用是弱引用。通过下面的WeakHashMapDemo.java,我们来看下它的用法。
1 import java.util.HashMap;
2 import java.util.Iterator;
3 import java.util.Map;
4 import java.util.WeakHashMap;
5 public class WeakHashMapDemo {
6 public static void main(String[] args) throws Exception {
7 String a = new String("a");
8 String b = new String("b");
9 Map weakmap = new WeakHashMap();
10 Map map = new HashMap();
11 map.put(a, "aaa");
12 map.put(b, "bbb");
13 weakmap.put(a, "aaa");
14 weakmap.put(b, "bbb");
15 map.remove(a);
16 a=null;
17 b=null;
18 System.gc();
19 Iterator i = map.entrySet().iterator();
20 while (i.hasNext()) {
21 Map.Entry en = (Map.Entry)i.next(); System.out.println("map:"+en.getKey()+":"+en.getValue());
22 }
23 Iterator j = weakmap.entrySet().iterator();
24 while (j.hasNext()) {
25 Map.Entry en = (Map.Entry)j.next();System.out.println("weakmap:"+en.getKey()+":"+en.getValue());
26 }
27 }
28 }
通过下表,我们来详细说明关键代码的含义。
行号 |
针对内存的操作以及输出结果 |
7 |
在堆空间里分配一块空间(假设首地址是1000),在其中写入String类型的a,并用a这个强引用指向这块空间。 |
8 |
在堆空间里分配一块空间(假设首地址是2000),在其中写入String类型的b,并用b这个强引用指向这块空间。 |
11,12 |
在HashMap里了插入两个键值对,其中键分别是a和b引用,这样1000号和2000号内存上就分别多加了一个强引用了(有两个强引用了)。 |
13,14 |
在WeakHashMap里了插入两个键值对,其中键分别是a和b引用,这样1000号和2000号内存上就分别多加了一个弱引用了(有两个强引用,和一个弱引用)。 |
15 |
从HashMap里移出键是a引用的键值对,这时1000号内存上有一个String类型的强引用和一个弱引用。 |
16 |
撤销掉1000号内存上的a这个强引用,此时1000号内存上只有一个弱引用了。 |
17 |
撤销掉2000号内存上的b这个强引用,此时2000号内存上有一个HashMap指向的强引用和一个WeakHashMap指向的弱引用。 |
18 |
通过System.gc()回收内存 |
19~22 |
遍历并打印HashMap里的对象,这里争议不大,在11和12行放入了a和b这两个强引用的键,在第15行移出a,所以会打印map:b:bbb。 |
23~25 |
遍历并打印WeakHashMap里的对象,这里的输出是weakmap:b:bbb。 虽然我们没有从WeakHashMap里移除a这个引用,但之前a所对应的1000号内存上的强引用全都已经被移除,只有一个弱引用,所以在第18行时,1000号内存里的内存已经被回收,所以WeakHashMap里也看不到a了,只能看到b。 |
根据上文和这里的描述,我们知道如果当一个对象上只有弱引用时,这个对象会在下次垃圾回收时被回收,下面我们给出一个弱引用的使用场景。
比如在某个电商网站项目里,我们会用Coupan这个类来保存优惠券信息,在其中我们可以定义优惠券的打折程度,有效日期和所作用的商品范围等信息。当我们从数据库里得到所有的优惠券信息后,会用一个List<Coupan>类型的coupanList对象来存储所有优惠券。
而且,我们想要用一种数据结构来保存一个优惠券对象以及它所关联的所有用户,这时我们可以用WeakHashMap<Coupan, <List<WeakReference <User>>>类型的weakCoupanHM对象。其中它的键是Coupan类型,值是指向List<User>用户列表的弱引用。
大家可以想象下,如果有100个优惠券,那么它们会存储于List<Coupan>类型的coupanList,同时,WeakHashMap<Coupan, <List<WeakReference <User>>>类型的weakCoupanHM对象会以键的形式存储这100个优惠券。而且,如果有1万个用户,那么我们可以用List<User>类型的userList对象来保存它们,假设coupan1这张优惠券对应着100个用户,那么我们一定会通过如下的代码存入这种键值对关系,weakCoupanHM.put(coupan1,weakUserList);,其中weakUserList里以弱引用的方式保存coupan1所对应的100个用户。
这样的话,一旦当优惠券或用户发生变更,它们的对应关系就能自动地更新,具体表现如下。
1 当某个优惠券(假设对应于coupan2对象)失效时,我们可以从coupanList里去除该对象,coupan2上就没有强引用了,只有weakCoupanHM对该对象还有个弱引用,这样coupan2对象能在下次垃圾回收时被回收,从而weakCoupanHM里就看不到了。
2 假设某个优惠券coupan3用弱引用的方式指向于100个用户,当某个用户(假设user1)注销账号时,它会被从List<User>类型的userList对象中被移除。这时该对象上只有weakCoupanHM里的值(也就是<List<WeakReference <User>>)这个弱引用,该对象同样能在下次垃圾回收时被回收,这样coupan3的关联用户就会自动地更新为99个。
如果不用弱引用,而是用常规的HashMap<Coupan,List<User>>来保存对应关系的话,那么一旦出现优惠券或用户的变更的话,那么我们就不得不手动地更新这个表示对应关系的HashMap对象了,这样,代码就会变得复杂,而且我们很有可能因疏忽而忘记在某个位置添加更新代码。相比之下,弱引用给我们带来的“自动更新“就能给我们带来很大的便利。
4 不能投机取巧,但面试确实有技巧
笔者写本文的意思,不是让大家投机取巧,事实上,如果大家只知道这些知识,而不知道其他虚拟机(或Java Core)相关的知识点,面试通过的可能性很低。
但话说回来,如果大家在平时开发时积累了很多经验,但不会总结,在面试时也无法很好地展示各种能力,这样也是非常可惜的。
根据本人在培训学校的经验,首先通过可能掌握各种Java技能,在这个基础上再讲述上述软引用和弱引用的技能,这些候选人得到的反馈是,至少在Java Core方面比较精通。
在本文的其它博客里,也列了相关面试技巧,欢迎大家看其它的文章。
通过软引用和弱引用提升JVM内存使用性能的方法(面试时找机会说出,一定能提升成功率)的更多相关文章
- 合理使用软引用和弱引用,提升JVM内存使用性能
在项目运行时,OOM异常是比较处理的,因为从日志看出的发生异常的代码点可能仅仅是最后一根稻草,从中可能未必能发现OOM的原因,而且OOM未必是固定重现的. 上医治未病,与其等OOM问题发生时再通过看日 ...
- 【JVM从小白学成大佬】3.深入解析强引用、软引用、弱引用、幻象引用
关于强引用.软引用.弱引用.幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用. 在java语言中, ...
- 【JVM学习】3.深入解析强引用、软引用、弱引用、幻象引用
来源:公众号:猿人谷 关于强引用.软引用.弱引用.幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用 ...
- JVM学习之强引用、弱引用、软引用、虚引用
转自:http://my.oschina.net/ydsakyclguozi/blog/404389 多谢博主分享 1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象 ...
- Android性能提升之强引用、软引用、弱引用、虚引用使用
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52637333 背景:收到公众投稿 ...
- 【案例演示】JVM之强引用、软引用、弱引用、虚引用
1.背景 想要理解对象什么时候回收,就要理解到对象引用这个概念,于是有了下文 2.java中引用对象结构图 3.引用详解 3.1.什么是强引用 a.当内存不足,JVM开始垃圾回收,对于强引用的对象,就 ...
- JVM强引用、软引用、弱引用、虚引用、终结器引用垃圾回收行为总结
JVM引用 我们希望能描述这样一类对象: 当内存空间还足够时,则能保留在内存中:如果内存空间在进行垃圾收集后还是很紧张,则可以抛弃这些对象. -[既偏门又非常高频的面试题]强引用.软引用.弱引用.虚引 ...
- Java学习之强引用,弱引用,软引用 与 JVM
1.java内存管理分为内存分配和内存回收,都不需要程序员负责. 2.垃圾回收的机制主要是看对象是否有引用指向该对象. java对象的引用包括 强引用 软引用 弱引用 虚引用 3.强引用 是指创建 ...
- Java对象引用/JVM分级引用——强引用、软引用、弱引用、虚引用
无论是通过引用计数法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判断对象是否存活都与“引用”有关, 相关资料:如何判断对象是否存活/死去 那么引用究竟是什么?让我们一起来看一下 ...
随机推荐
- 芝麻HTTP:Python爬虫入门之Cookie的使用
为什么要使用Cookie呢? Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密) 比如说有些网站需要登录后才能访问某个页面,在登录之前,你想抓 ...
- C# MVC的一种高效分页的html方法
首先创建一个html的扩展方法,这个方法是万能的,可以直接拿到您的项目中使用: //主要就是输出分页的超级链接的标签 //自定义分页Helper扩展 public static HtmlString ...
- iOS - Quartz 2D 画板绘制
1.绘制画板 1.1 绘制简单画板 PaintBoardView.h @interface PaintBoardView : UIView @end PaintBoardView.m @interfa ...
- 第二篇:使用Spark对MovieLens的特征进行提取
前言 在对数据进行了初步探索后,想必读者对MovieLens数据集有了感性认识.而在数据挖掘/推荐引擎运行前,往往需要对数据预处理.预处理的重要性不言而喻,甚至比数据挖掘/推荐系统本身还重要. 然而完 ...
- 【BZOJ1012】【JSOI2008】最大数(线段树)
[JSOI2008]最大数 题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当前 ...
- 实战绕过某医院的waf
最近遇到一个注入,我们直接来看吧.还是常规的单引号: 是一个很常规的注入.我们来尝试下获取一些信息: 然后发现是有防火墙的,安全狗.安全狗有很多针对php+mysql的绕过方法,比如这样:/*!uni ...
- linux升级python3.6相关命令
sudo apt-get install python3.6 sudo update-alternatives --install /usr/bin/python python /usr/bin/py ...
- webpack深入场景——开发环境和生产环境配置
以前自己写一小项目时,webpack的配置基本就是一套配置,没有考虑生产环境和开发环境的区分,最近在做一个复杂的商城项目接触到了webpack的高级配置,经过两天的研究,写出了一份目前来说比叫满意的配 ...
- eclipse中Maven项目pom.xml报错:com.thoughtworks.xstream.io.HierarchicalStreamDriver
eclipse中创建Maven项目时 pom.xml报错:com.thoughtworks.xstream.io.HierarchicalStreamDriver 解决方案1.在pom文件中加入mav ...
- jQuary学习の二の语法
jQuery 语法是通过选取 HTML 元素,并对选取的元素执行某些操作.基础语法: $(selector).action() 美元符号定义 jQuery 选择符(selector)"查询& ...