在Java里面,是不需要太过于关乎垃圾回收,但是这并不意味着开发者可以不了解垃圾回收的机制,况且在java中内存泄露也是家常便饭的事情。因此了解垃圾回收的相关知识就显得很重要了。

引用,在垃圾回收中是一个很关键的概念,它关系到如何辨别这个对象是否被回收,什么时机回收。

引用的类型

在Java中引用的类型可以分为四个类型,依次是:

  • 强引用:在任何时间JVM都不会进行回收
  • 软引用:在内存不够的时候,JVM会进行回收
  • 弱引用:只要进行垃圾回收,就会触发回收
  • 虚引用:不知道啥时候就被回收了,可以理解为没引用一个样

因此,按照JVM对他们回收的几率从小到大依次为:

强引用<软引用<弱引用<虚引用

也就是说JVM对强引用的回收能力最小,对虚引用的回收能力最大。

引用分类的作用

一般我们编写的代码都是强引用的,比如:

Person p = new Person();
Person p1 = p;

pp1都指向了创建的Person对象,他们都是强引用的。如果想要回收这个对象,只有p1p都指向null后,才可以。

那么,有一些场景下往往引用清除的不及时,就会造成内存泄露,一些对象无法回收;无法回收的对象如果积累很多,就会造成OUT OF MEMORY——OOM.

举个例子,在很多的代码里面都喜欢用Map作为内存缓存的容器,如果你写出了这样的代码:

Map<String,Object> map = new HashMap<String,String>();
while(true){
Object value = new XXX();
map.add(key,value);
value = null;
}

虽然说,后面的value被设置成Null,但是map里面却仍然保留了对象的引用,因此这个对象实际上是无法被回收的。

做个测试:

public class WeakTest {
static final int MB = 1024 * 512; static String createLongString(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
sb.append('a');
sb.append(System.nanoTime());
return sb.toString();
} public static void main(String[] args) {
Map<Integer,String> substrings = new HashMap();//强引用的Map
for(int i=0; i< 1000000; i++){
String longStr = createLongString(MB);
substrings.put(i,longStr);
// longStr = null;
// substrings.remove(i);
System.out.println(i);
}
}
}

如果注释的两句话不被打开,那么很快就会内存溢出。除非你两边都去解除应用,可想而知,程序员做这种工作实在是太痛苦了。

不要担心,这个时候就可以应用到上面的不同类型的引用知识了

在Java里面,JDK为我们提供了一个弱引用的集合,WeakHashMap。它会在垃圾回收的时候尝试回收集合里面的对象。当然根据垃圾回收的时机,也可以选择软引用的集合。

public static void main(String[] args) {
Map<Integer,String> substrings = new WeakHashMap();//弱引用的Map
for(int i=0; i< 1000000; i++){
String longStr = createLongString(MB);
substrings.put(i,longStr);
System.out.println(i);
}
}

这样就不担心内存溢出了。

场景设想

比如,你的系统需要引用大量的资源相关的缓存,但是还没有引入redis等缓存系统,那么就可以使用这种方式。

虚引用

虚引用的使用场景就比较鸡肋了,我也想不出什么时候会使用它。但是它跟其他的引用都有一种场景,就是在垃圾回收的时候,把引用放在回收队列里面,针对这个队列可以做一些操作。这种方式比finalize()要文档的多..

public class PhantomTest {
public static boolean isRun = true; public static void main(String[] args) throws Exception {
String abc = new String("abc");
System.out.println(abc.getClass() + "@" + abc.hashCode());
final ReferenceQueue referenceQueue = new ReferenceQueue<String>();
new Thread() {
public void run() {
while (isRun) {
Object o = referenceQueue.poll();
if (o != null) {
try {
Field rereferent = Reference.class.getDeclaredField("referent");
rereferent.setAccessible(true);
Object result = rereferent.get(o);
System.out.println("gc will collect:"+ result.getClass() + "@"+ result.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}.start();
PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,referenceQueue);
abc = null;
Thread.currentThread().sleep(3000);
System.gc();
Thread.currentThread().sleep(3000);
isRun = false;
} }

首先需要创建一个引用队列:

final ReferenceQueue referenceQueue = new ReferenceQueue<String>();

创建虚引用,并关联到引用队列

PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,referenceQueue);

等引用被回收的时候,就会在Object o = referenceQueue.poll();取到对象引用了。

虽然一般不会有这种底层的使用场景,但是了解一点总归是好的。

Java程序员的日常—— 垃圾回收中引用类型的作用的更多相关文章

  1. Java程序员的日常——经验贴(纯干货)

    工作当中遇到的事情比较杂,因此涉及的知识点也很多.这里暂且记录一下,今天遇到的知识点,纯干货~ 关于文件的解压和压缩 如果你的系统不支持tar -z命令 如果是古老的Unix系统,可能并不认识tar ...

  2. Java程序员的日常—— 《编程思想》关于类的使用常识

    Java虽然利用JVM,让程序员可以放心大胆的使用,可是仍然会出现内存泄露等问题.世上没有绝对的银弹,因此也不能完全把所有的任务都交给JVM,了解Java中的初始化与垃圾回收还是必不可少的知识. 关于 ...

  3. Java程序员的日常—— 基于类的策略模式、List<?>与List、泛型编译警告、同比和环比

    早晨起得太早,昨晚睡得太晚,一天都迷迷糊糊的.中午虽然睡了半个小时,可是依然没有缓过来.整个下午都在混沌中....不过今天下载了一款手游--<剑侠情缘>,感觉不错,喜欢这种类型的游戏. 今 ...

  4. Java程序员的日常—— Properties文件的读写

    在日常的Java程序开发中,Properties文件的读写是很常用的.经常有开发系统通过properties文件来当做配置文件,方便用户对系统参数进行调整. 那么本片就来简单的介绍下,如何使用Prop ...

  5. Java程序员的日常 —— 多进程开发

    最近再弄进程管理相关的工作,因此必要的就涉及到各种系统下关于进程的管理. 这里简单的介绍下: 如何在Java中执行命令 在windows下肯定是dos命令了,而在linux则为shell命令.执行的方 ...

  6. Java程序员的日常—— Arrays工具类的使用

    这个类在日常的开发中,还是非常常用的.今天就总结一下Arrays工具类的常用方法.最常用的就是asList,sort,toStream,equals,copyOf了.另外可以深入学习下Arrays的排 ...

  7. Java程序员的日常 —— static的用法讲解实践

    之前文章说过Java中static的作用,有朋友想看个例子.于是便抽空写了个小栗子 代码 package xing.test.thinking.chap5; class A{ public A() { ...

  8. Java程序员的日常 —— Java类加载中的顺序

    之前说过Java中类的加载顺序,这次看完继承部分,就结合继承再来说说类的加载顺序. 继承的加载顺序 由于static块会在首次加载类的时候执行,因此下面的例子就是用static块来测试类的加载顺序. ...

  9. Java程序员的日常——存储过程知识普及

    存储过程是保存可以接受或返回用户提供参数的SQL语句集合.在日常的使用中,经常会遇到复杂的业务逻辑和对数据库的操作,使用存储过程可以进行封装.可以在数据库中定义子程序,然后把子程序存储在数据库服务器, ...

随机推荐

  1. EXCEL表格常用函数使用的难点

    1.  INDIRECT(ref_text,逻辑值) 返回由文本字符串指定的引用.此函数立即对引用进行计算,并显示其内容.ref_text,文本引用说明, (1) A1-样式的引用(逻辑值,T,缺省) ...

  2. hibernate_validator_07

    一.校验组序列 默认情况下,约束的验证是没有一定的顺序的,不管他们是属于哪个认证组的.但是在有些环境中,我们控制这些约束验证的顺序还是很有用的. 就拿我们上一个例子来说,我们可以这样:首先在我们检查车 ...

  3. openssl 使用非阻塞 bio

    序 在项目中需要访问 https 加密的网页,为了保证并发性,需要用到非阻塞的 socket,搜索发现,这种使用场景的相关介绍不是很多,所以这里记录一下使用的过程. 在项目中,所使用的 ssl 库是老 ...

  4. SAS学习笔记

    一.            在SAS中进行随机抽样: 1. 在实际数据处理中常常需要进行样本抽样,在实践中主要有两种情况: (1)简单无重复抽样(2)分层抽样   a.等比例分层抽样  b. 不等比例 ...

  5. 3DMAX-模型制作简介

    本文由博主(YinaPan)原创,转载请注明出处:http://www.cnblogs.com/YinaPan/p/3dmax_moxingzhizuo.html  一.快捷键: ALT + X 透明 ...

  6. SQL中varchar和nvarchar有什么区别?

    varchar(n)长度为 n 个字节的可变长度且非 Unicode 的字符数据.n 必须是一个介于 1 和 8,000 之间的数值.存储大小为输入数据的字节的实际长度,而不是 n 个字节.nvarc ...

  7. photpshop渐变玩法_学习教程

  8. recvmsg和sendmsg函数

    这两个函数是最通用的I/O函数.实际上我们把所有read.readv.recv和recvfrom调用替换成recvmsg调用.类似地,各种输出函数调用也可以替换成sendmsg调用 #include ...

  9. .net别样外观控件包DotNetBar

    内容介绍:http://www.componentcn.com/?thread-6423-1.html BubbleBar应用: BubbleBar, DevComponents.    Namesp ...

  10. (转) IsPostBack的用法

    某一天在网上看到有人写了这样一段代码: protected void Page_Load(object sender, EventArgs e)     {         if (!IsPostBa ...