在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();取到对象引用了。

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

最新文章

  1. 关于一些学习html和css的笔记
  2. SignalR实现服务器与客户端的实时通信
  3. PHP笔试题(转载)
  4. 避免ajax中get方法产生缓存的解决办法
  5. XML JSON解析--基本功能
  6. dedecms代码研究一
  7. Teamwork——Week4 团队项目之NABC
  8. C#基础|面向对象之继承
  9. 一个PDO类
  10. MySQL 对于大表(千万级),要怎么优化呢?
  11. Git push 时每次都需要密码的疑惑
  12. 病毒侵袭 - HDU 2896(AC自动机)
  13. linux中screen命令的用法
  14. idea15破解
  15. Spark开发指南
  16. VBS自编写脚本。(实现批量修改文件名且在执行前,备份原有文件夹中的文件)
  17. 支持缩放的fresco图片控件 —— fresco sample: ZoomableDraweeView
  18. 浏览器与android移动端视频互播技术实现
  19. Groovy学习笔记(1)读取CSV文件
  20. Unity3D——SendMessage方法的使用

热门文章

  1. asp.net C# 导出EXCEL数据
  2. 用js给html设置style
  3. (转)PHP函数set_magic_quotes_runtime()的作用
  4. 静态html传参数
  5. C#串口控制
  6. Ajax的工作流程简述
  7. zepto源码研究 - callback.js
  8. cas sso单点登录系列2:cas客户端和cas服务端交互原理动画图解,cas协议终极分析
  9. 用JAVA给JSON进行排版
  10. 修路方案(nyoj)