垃圾回收的基本思想就是判断一个对象是否可触及性,说白了就是判断一个对象是否可以访问,如果对象对引用了,说明对象正在被使用,如果发现对象没有被引用,说明对象已经不再使用了,不再使用的对象可以被回收,但是不一定立马被回收,取决于GC垃圾回收的算法。

判断对象的可触及性。

1.可以触及的:从根节点开始,可以到达这个对象,说明这个对象还在使用。

2.可复活的:对象的所有引用都被释放,但是对象可能在finalize()方法复活了。

3.不可触及的:对象的finalize()被调用,但是没有复活,那就彻底挂了,进入不可触及的状态。

不可触及的对象能复活吗?答案是不能,为啥,因为finalize()方法只会调用一次。

上面的三种情况只有不可触及的对象才可以被收回。

1.1. 对象的复活

对象的复活跟人的生老病死一样,在你临死的时候还有可能回光返照一次,如果成功了就复活了。所以想复活只抓住这一次机会,程序的回光返照就是finalize()方法,因为finalize()只会执行一次。

这里给出例子,演示这种情况,看下面的例子:

public class CanObj {
public static CanObj canObj;
@Override
public String toString() {
return " i am canObj";
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
this.canObj=this;
}
public static void main(String[] args) throws Exception {
canObj=new CanObj();
canObj=null;
System.gc();
Thread.sleep(1000);
if (canObj==null) {
System.out.println("canObj  null ");
}else {
System.out.println("canObj  is not null");
}
System.out.println("第二次GC");
canObj=null;
System.gc();
Thread.sleep(1000);
if (canObj==null) {
System.out.println("canObj  null ");
}else {
System.out.println("canObj  is not null");
}
}
}

运行以上代码,输出如下:

finalize

canObj  is not null

第二次GC

canObj  null

说明确实触发finalize()方法了,而且只调用一次。

1.1.1. finalize说明

1.finalize()方法推荐不要使用

2.finalize()可能复活对象,可能发生引用外泄。

3.finalize()是系统调用的。所以调用时间是不确定的。所以资源的释放最好放在try catch finally中。

1.2. 引用和强、软、弱、和虚强度

java提供了四个级别的引用分别是强引用、软引用、弱引用和虚引用,强引用我们平时使用的就是比如new B()就是个强引用,其他的几种引用都可在在java.lang.ref.Reference找到他们的影子,如图显示了3中引用类型对应的类图关系,开发人员直接使用它们。

1.2.1. 强引用

强引用就是程序中一般的引用类型,强引用对象是可触及的,不会被回收,其他的几种就是在一定情况下是被回收的。

下面是一个强引用的例子:

StringBuffer str=new StringBuffer("hello shareniu");

上面的代码是在函数内可以运行的,那么局部变量str被分配在栈上,对象StringBuffer分配在堆上,局部变量str指向StringBuffer的堆空间,通过str可以操作实例,那么str就是StringBuffer实例的强引用如下图所示:

此时,在运行复制语句:

StringBuffer str1=str;

那么,str所指向的对象也将被str1指向,同事局部变量表也会存放str1的变量如下图所示:

System.out.println(str==str1);

相等的==比较的是操作数指向的堆空间的地址是否相等。

强引用特点如下:

1.强引用可以直接访问目标对象。

2.强引用对象任何时候都不会被系统回收,虚拟机抛出OOM异常也不会回收强引用对象。

3.强引用对象可能造成内存泄漏。

1.2.2. 软引用--可以被回收的引用

一个对象持有软引用,那么当堆空间不足的时候,就会回收这块区域。软引用实现类java.lang.ref.SoftReference<T>

下面演示软引用在系统堆内存不足的时候被回收。

public class SoftRef {
public static class User{
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
User user=new User(1, "shareniu");
SoftReference<User> usSoftReference=new SoftReference<SoftRef.User>(user);
user=null;
System.out.println(usSoftReference.get());
System.gc();
System.out.println("after gc");
System.out.println(usSoftReference.get());
byte []b=new byte[7*925*1024];
System.gc();
System.out.println(usSoftReference.get());
}
}

配置参数-Xmx10m
分配10M内存所以堆空间内存不足的时候,软引用被回收。

程序输出如下:

User [id=1, name=shareniu](第一次从软引用获取到值)

after gc

User [id=1, name=shareniu](GC没有清除软引用,因为堆空间还有)

null(被清除了,因为堆空间慢了)

实验得出结论:GC不一定会回收软引用对象,但是内存不足的时候软引用会被回收,所以软引用对象不会OOM异常。

软引用的时候可以构造一个队列,当软引用对象被回收的时候,就会加入引用队列,通过对了可以追踪对象的回收情况。(在下面的通用例子中会降到)

1.2.3. 弱引用--发现即回收

在系统GC的时候,只要发现弱引用-不管系统堆内存是否使用,都会将对象进行回收,但是垃圾回收器的线程优先级比较低,所以不一定很快就能发现持有的弱引用对象。所以弱引用-可能存在比较长的时间。一旦一个弱引用-对象被回收,变回加入到注册的引用队列中
弱引用-具体的实现类java.lang.ref.WeakReference<T>

下面的例子显示弱引用的特点:

public class WakRef {
public static class User{
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
User user=new User(1, "shareniu");
WeakReference<User> weakReference=new WeakReference<WakRef.User>(user);
user=null;
System.out.println(weakReference.get());
System.gc();
System.out.println("after gc");
System.out.println(weakReference.get());
}
}

程序输出如下:

User [id=1, name=shareniu]

after gc

null

可以看出,在GC之后,弱引用立即被清除了。

弱引用的时候可以构造一个队列,当弱引用对象被回收的时候,就会加入引用队列,通过对了可以追踪对象的回收情况。(在下面的通用例子中会降到)

1.2.4. 虚引用--对象回收追踪

虚引用是所有引用类型中最弱的一个,一个持有虚引用对象。和没有引用基本一样,随时都有可能被回收,当试图使用虚引用的get()获取的时候,总会失败报错,不能获取到。虚引用必须和引用队列一起使用,它的作用就是跟踪垃圾回收的过程。

当垃圾回收器回收一个对象的时候,如果发现他是虚引用,会立马讲这个虚引用的对象加入引用队列,通知应用程序对象的回收情况。所以我们在这里看一下队列如何使用。(除了强引用不能使用队列,其他的三个都可以使用,虚引用是必须使用队列

下面给出一个实例,使用虚引用跟踪一个可以复活的对象的回收。

public class TraceObj {
@Override
public String toString() {
return "TraceObj ";
}
public static TraceObj obj;
//定义一个队列,对象回收时候会进入这个队列
static ReferenceQueue<TraceObj> queue=null;
public static class CheckRefQuene extends Thread{
@Override
public void run() {
while (true) {
if (queue!=null) {
PhantomReference<TraceObj> phantomReference=null;
try {
phantomReference=(PhantomReference<TraceObj>) queue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (phantomReference!=null) {
System.out.println(" delete "+phantomReference);
}
}
}
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("复活一个对象");
//复活一个对象 finalize只会执行一次
obj=this;
}
public static void main(String[] args) throws Exception {
Thread thread=new CheckRefQuene();
thread.setDaemon(true);
thread.start();
queue=new ReferenceQueue<TraceObj>();
obj=new TraceObj();
PhantomReference<TraceObj> phantomReference=new PhantomReference<TraceObj>(obj, queue);
obj=null;
System.gc();
Thread.sleep(1000);
if (obj==null) {
System.out.println("null ");
}else {
System.out.println("obj is not null ");
}
System.out.println("第二次gc");
obj=null;
System.gc();
Thread.sleep(1000);
if (obj==null) {
System.out.println("null ");
}else {
System.out.println("obj is not null ");
}
}
}

程序的输出如下:

复活一个对象

obj is not null

第二次gc

delete java.lang.ref.PhantomReference@1d86fd3  (删除的对象确实进入队列了)

null

虚引用对象可以跟踪对象的回收时间,所以,可以将一些资源释放操作放在虚引用中执行和记录。

1.2.5. 比较

软、弱、和虚 引用都可以放置到队列中,强引用不需要,虚引用必须要使用队列。

软引用可以放一些缓存的数据,当内存不足的时候被回收

软引用在设备内存比较少的时候特别有用,比如android系统。

一个android应用如果设计到通过网络获取图片,为了让系统更快的运行和更节省流量我们可以将已经下载下来的图片缓存起来,当第二次浏览到该图片时就可以从缓存中拿。

缓存的方式有:一是放在系统内存中这样效率最高,二是把文件写到外部存储器上。但是就目前而言android系统的内存是非常的有限的不可能像PC机那样配置那么高的内存,而且外部存储器的容量也是有限的。

如何我们用SoftReference的方式存储在内存中是一中很好的解决方法(当然不止这一种)。

虚引用对象可以跟踪对象的回收时间,所以,可以将一些资源释放操作放在虚引用中执行和记录。

GC真正的垃圾:强、软、弱、和虚 对象的更多相关文章

  1. java中强,软,弱,虚引用 以及WeakHahMap

    java中强,软,弱,虚引用  以及WeakHahMap   一:强软引用: 参考:http://zhangjunhd.blog.51cto.com/113473/53092/进行分析   packa ...

  2. java中的强,软,弱,虚引用

    引用的应用场景 我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型.这正是Java中弱引用和软引用的主要区别. 如果一个对象只有弱引用指向 ...

  3. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象

    减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...

  4. JVM-gcRoots 和 强引用,软引用, 弱引用, 虚引用, 代码演示和应用场景

    什么是垃圾? 什么是gcRoots, 谈谈你对 强, 软, 弱 , 虚引用的理解, 他们的应用场景 jvm采用可达性分析法: 从gcRoots集合开始,自上向下遍历,凡是在引用链上的对象,都不是垃圾, ...

  5. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  6. Java:对象的强、软、弱、虚引用

    转自: http://zhangjunhd.blog.51cto.com/113473/53092 1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...

  7. 【转载】 Java 7之基础 - 强引用、弱引用、软引用、虚引用

    原文地址:http://blog.csdn.net/mazhimazh/article/details/19752475 1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一 ...

  8. Java中四种引用:强、软、弱、虚引用

    这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...

  9. Java:对象的强、软、弱和虚引用

    1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...

随机推荐

  1. 【NOIP 2017】列队

    Description Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵的行数为 n ...

  2. bzoj 3672: [Noi2014]购票

    Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的 ...

  3. ●洛谷P3242 [HNOI2015]接水果

    题链: https://www.luogu.org/problemnew/show/P3242 题解: 整体二分,扫描线+树状数组. 详细的题解:http://blog.csdn.net/thy_as ...

  4. ●Codevs 4158 残缺的字符串

    题链: http://codevs.cn/problem/4158/ 题解: FFT. 定义两个相同长度的字符串s1,s2的距离为 $$dis(s1,s2)=\sum_{i=0}^{len-1}(s1 ...

  5. 【HDU 2966 k-dimensional Tree 入个门】

    ·“k-d树是一种分割k维数据空间的数据结构.主要应用于多维空间关键数据的范围搜索和最近邻搜索……”’'   ·英文题,述大意:      给出平面内n个点(n<=100000,0<=x, ...

  6. 【uva 1411 Ants蚂蚁们】

    题目大意: ·给你一个n,表示输入n个白点和n个黑点(输入每一个点的坐标).现在需要将各个白点和各个黑点一一用线段连接起来,需要满足这些线段不能够相交. ·特色: 我们如何保证线段间不相交. ·分析: ...

  7. sklearn.model_selection 的 train_test_split作用

    train_test_split函数用于将数据划分为训练数据和测试数据. train_test_split是交叉验证中常用的函数,功能是从样本中随机的按比例选取train_data和test_data ...

  8. SQL优化实用方法

    SQL优化:避免索引失效 1.不使用NULL 任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的.因为只有该字段中有null值,即使创建了索引其实也是没有用 ...

  9. IOS charles抓包HTTP

    charles通常用来截取本地的网络封包,但也可以用它来截取其他设备上的网络请求.本篇以IOS为例,讲解如何进行相应的操作. 1.charles上的设置 要截取iphone上的网络请求,我们要先将ch ...

  10. java线程与进程

    Java线程与进程 进程与线程的关系 进程里面至少有一个线程,进程间的切换会有较大的开销 线程必须依附在进程上,同一进程共享代码和数据空间 多线程的优势 多线程可以达到高效并充分利用cpu 线程使用的 ...