垃圾回收的基本思想就是判断一个对象是否可触及性,说白了就是判断一个对象是否可以访问,如果对象对引用了,说明对象正在被使用,如果发现对象没有被引用,说明对象已经不再使用了,不再使用的对象可以被回收,但是不一定立马被回收,取决于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. [SDOI 2008]Cave 洞穴勘测

    Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...

  2. [HAOI 2007]理想的正方形

    Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第 ...

  3. 【luogu P4005 清华集训2017】小Y和地铁

    题目描述 小 Y 是一个爱好旅行的 OIer.一天,她来到了一个新的城市.由于不熟悉那里的交通系统,她选择了坐地铁. 她发现每条地铁线路可以看成平面上的一条曲线,不同线路的交点处一定会设有 换乘站 . ...

  4. hdu 5429(大数模板)

    题意:判断是否是等比数列 a[i] * a[i] = a[i+1] * a[i-1]   +   部分为0 的情况 /*  表示还没学java.... ,模板来自NK_test #include &l ...

  5. kafka快速入门

    一.kafka简介 kafka,ActiveMQ,RabbitMQ是当今最流行的分布式消息中间件,其中kafka在性能及吞吐量方面是三者中的佼佼者,不过最近查阅官网时,官方与它的定义为一个分布式流媒体 ...

  6. scanf———while(scanf ("%lu",&num) = =1)什么意思

    scanf的返回值由后面的参数决定 scanf("%d%d", &a, &b); 如果a和b都被成功读入,那么scanf的返回值就是2 如果只有a被成功读入,返回值 ...

  7. java 左移 右移

    public class test{ public static void main(String[] args) { int m = 9; int n = m >> 3; int p = ...

  8. C语言 字符串 字符串处理操作 字符串与函数

    字符数组的定义和初始化 宏常量+1  强调了字符串的最大强度 推荐忽略长度的定义 不能对所指向的存储单元内容修改,除非是字符串数组的定义 因为指针变量指向的是字符串数组的值,可以被修改. 未初始化 字 ...

  9. php中AJAX请求中使用post和get请求的区别

    之前使用这两个请求的时候,主要从几个方面考虑: 1.语义,get就是从服务器获取,一般就是获取/查询资源信息.post就是提交给服务器,一般就是更新资源信息. 2.请求文件大小,get一般只有2k这样 ...

  10. Python中capitalize()与title()的区别

    capitalize()与title()都可以实现字符串首字母大写.主要区别在于:capitalize(): 字符串第一个字母大写title(): 字符串内的所有单词的首字母大写 例如: >&g ...