垃圾回收特点

  • 垃圾:程序运行过程中,会为对象、数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用
  • Java的垃圾回收机制只回收堆内存中的对象,不回收数据库连接、IO等资源,所以才要在finally中关闭
  • 要回收,但什么时候回收是不一定的,即使显式的调用了System.gc()
  • 垃圾回收程序在真正回收之前,会先调用被回收对象的finalize()方法,这是Object的protected方法,每个类都要继承的,这个方法可能导致这个被回收对象复活,就是让另一个引用指向它,因而就取消回收。此处存疑?垃圾回收程序是否一定会执行finalize()方法,还是不一定?
  • 特别注意:垃圾回收不会回收在常量池中的String的直接量

对象在内存中的状态

  • 可达状态:如果一个对象有一个或以上的引用指向它,那它就是可达的,可以通过引用变量访问到它的实例变量、实例方法等
  • 可恢复状态:没有引用指向某个对象了,那它就成了可恢复状态。可恢复的意思是调用finalize()方法时,可能能让一个引用变量重新指向它,从而变成可达状态
  • 不可达状态:调用finalize()方法后,也没能让一个对象变回可达状态,那这个对象就处于不可达状态,然后就等着被真正的回收吧

对象被哪些变量引用

  • 被类变量引用:该类被销毁,被引用对象才变为可恢复
  • 被实例变量引用:该实例变量所属的对象被销毁,被引用对象才变为可恢复
  • 被局部变量引用:切换二者的指向关系时,被引用对象变为可恢复

何时回收

  • 垃圾回收程序会不定时的运行,我们完全不能把握,最多就是通知它一声“麻烦你来回收垃圾吧”

    • System.gc()
    • Runtime.getRuntime().gc():每个Java程序都有一个与之对应的Runtime实例,应用程序通过该类与其运行环境关联,但不能创建自己的Runtime实例,但可以通过静态方法getRuntime()返回一个。
  • 比如下面的代码,我运行了20次,垃圾回收也没运行一次
package testpack;
public class Test1{
public static void main(String[] args) {
for (int i=0;i<4;i++){
new Test1();
}
}
public void finalize(){
System.out.println("垃圾回收前的finalize()方法运行中");
}
}
  • 再运行下面的代码,加了一行System.gc(),就是每次创建对象之后都通知一次gc(),结果运行的次数0~4次都有
package testpack;
public class Test1{
public static void main(String[] args) {
for (int i=0;i<4;i++){
new Test1();
System.gc(); //增加了这一行,通知gc()回收垃圾
}
}
public void finalize(){
System.out.println("垃圾回收前的finalize()方法运行中");
}
}

finalize()方法

  • Java的垃圾回收和finalize()方法比较复杂,看:http://www.cnblogs.com/iamzhoug37/p/4279151.html
  • 该方法是Object的protected方法,每个类都会继承
  • 不要自己调用该方法,该方法应该只由垃圾回收机制调用
  • finalize()方法不一定何时被调用,还有可能不会被调用?存疑!
  • finalize()方法运行后,可能使一个对象从可恢复变为可达
  • finalize()方法出现异常的话,垃圾回收机制不报异常,继续运行
  • 看示例代码:
package testpack;
import java.io.IOException;
import java.io.PrintStream;
public class Test1{
public static void main(String[] args) throws IOException{
PrintStream ps=new PrintStream("E:\\Temp\\output.txt");
PrintStream ini=System.out; //将标准输出保存下来,后面才好恢复
System.setOut(ps); //下面的输出比较长,控制台显示不全,将标准输出重定向到output.txt
for (int i=1;i<=10000;i++){
new Test1(i); //对象创建后就进入可恢复状态
System.gc(); //通知系统垃圾回收,何时回收不确定
System.runFinalization(); //该方法是强制系统立即调用可恢复对象的finalize()方法,跟System.gc()配合使用,这里进行了10000次循环,每次都运行了finalize()方法,但API文档中写的是`suggest`,不是一定。如果不调用这个方法,finallize()方法不一定何时调用。此处比较复杂,本例未必正确。
System.out.println("当前运行第 "+i+" 轮循环"+" 当前t1指向第 "+t1.num+" 个循环创建的对象");
}
ps.close();
System.setOut(ini);
System.out.println("运行结束");
}
public int num;
public Test1(int num){
this.num=num;
}
private static Test1 t1=null;
public void finalize(){
t1=this; //finalize()方法运行后,可恢复对象变为可达对象
}
}

强、软、弱、虚引用

  • 强引用StrongReference:

    • 常见的一般都是这种强引用
  • 软引用SoftReference:
    • 软引用指向的对象,有可能被回收,看内存够不够,不够的话,有可能回收
    • 多用于内存敏感的程序中
    • 通过SoftReference类实现
    • 包含一个get()方法,用来获得被它引用的对象
  • 弱引用WeakReference:
    • 跟软引用很像,但比弱引用还低,系统回收垃圾时,不管内存够不够,都要回收弱引用
    • 通过WeakReference类实现
    • 包含一个get()方法,用来获得被它引用的对象
  • 虚引用PhantomReference:
    • 虚引用就跟没有被引用差不多,主要用于跟踪对象被回收的状态
    • 不能单独使用,得跟引用队列java.lang.ref.ReferenceQueue联合使用
    • 包含一个get()方法,但总是返回null,就是不想你再"复活"一个对象
  • java.lang.ref.ReferenceQueue
    • 用于保存被回收后对象的引用
    • 系统在回收被引用的对象之后,将把被回收对象对应的引用添加到关联的引用队列中
  • 弱引用示例:
package testpack;
import java.lang.ref.WeakReference;
public class Test1{
public static void main(String[] args){
String s1=new String("ABCDEFG");
WeakReference wf=new WeakReference(s1);
s1=null; //切断强引用
System.out.println(wf.get()); //通过get()方法获得引用的对象
System.gc();
System.runFinalization();
System.out.println(wf.get()); //返回null
}
}
  • 虚引用示例:跟ReferenceQueue配合使用
package testpack;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class Test1{
public static void main(String[] args){
String s1=new String("ABCDEFG");
ReferenceQueue rq=new ReferenceQueue();
PhantomReference pr=new PhantomReference(s1,rq);
s1=null; //切断强引用,保留了虚引用
System.out.println(pr.get()); //返回null,虚引用的get()方法总是返回null,不能返回被引用对象
System.gc();
System.runFinalization(); //finalize()运行后,虚引用对象将被添加到引用队列上
System.out.println(rq.poll()==pr); //.poll()表示引用队列上的第一个被引用对象
}
}

关于String常量池的再思考

  • 之前在:0024 Java学习笔记-面向对象-包装类、对象的比较、String常量池问题中说过,String的intern()方法的作用是:返回这个字符串在常量池中的地址,常量池中如果有了这个字符串,那就返回其地址;如果没有,那就这个字符串在堆内存中的地址,添加到常量池表里面,再返回其地址,相当于返回的地址通过常量池表间接的指向了堆内存中的地址。
  • 下面做进一步的验证:
  • 先看代码:
package testpack;
public class Test1{
public static void main(String[] args){
String s1="A";
String s2="B";
String s3=s1+s2;
System.out.println("AB"==s3); //返回false,说明s3=s1+s2这行代码创建的"AB"对象位于堆内存中,否则应该返回ture
}
}
  • 再看第二段代码
package testpack;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class Test1{
public static void main(String[] args){
String s1="A";
String s2="B";
String s3=s1+s2; //s3指向堆内存中的"AB"
String s4=s3.intern(); //intern()方法将堆内存中的"AB"的地址添加到常量池表中,s4通过常量池表间接的指向堆内存中的"AB"
System.out.println(s3==s4); //true。s3和s4直接和间接指向堆内存的"AB",因而返回ture
System.out.println(s4=="AB"); //true。此处的直接量"AB"实际指向了堆内存中的"AB",因而返回true
ReferenceQueue rq=new ReferenceQueue();
PhantomReference pr=new PhantomReference(s3,rq); //创建虚引用,将堆内存中的"AB"跟引用队列关联起来
s3=null; //切换s3和堆内存中"AB"对象的强引用
System.gc();
System.runFinalization(); //这里将会运行finalize()方法,只有虚引用的对象都将被添加到引用队列上。如果堆内存中的"AB"的强引用全部被切断,那么"AB"将被添加到队列上
System.out.println(s4); //输出"AB"。说明堆内存中的"AB"还存在
System.out.println(rq.poll()==pr); //输出false。说明运行了finalize()方法后,堆内存中的"AB"没有被添加到引用队列上,从而说明还有强引用指向它,那就是s4. //如果没有s4=s3.intern()这句,再把下面包含s4的语句全注释掉,最后一行输出true,说明没有强引用指向"AB"了
}
}

0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用的更多相关文章

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

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

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

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

  3. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  4. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  5. 0025 Java学习笔记-面向对象-final修饰符、不可变类

    final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...

  6. 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类

    static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...

  7. (转)《深入理解java虚拟机》学习笔记3——垃圾回收算法

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  8. java学习之(垃圾回收)

    程序无法精确控制java垃圾回收的时机,但依然可以强制系统进行垃圾回收--这种强制只是通知系统进行垃圾回收, 但系统是否进行垃圾回收依然不确定.大部分时候,程序强制系统垃圾回收后总会有一些效果,强制系 ...

  9. 0020 Java学习笔记-面向对象-变量

    变量分为哪些 成员变量:类里面,方法外面定义的变量 实例变量:没有用static修饰的变量,属于对象:存在期:创建实例-销毁实例:作用域:与该实例的生存范围相同 类变量:用static修饰的变量,属于 ...

随机推荐

  1. 会务准备期间材料准备工作具体实施总结 ----(vim技巧应用, python信息提取与整合, microsoft word格式调整批量化)

    会务准备期间材料准备工作具体实施总结(vim, python, microsoft word) span.kw { color: #007020; font-weight: bold; } code ...

  2. React Native at first sight

    what is React Native? 跟据官方的描述, React Native是一套使用 React 构建 Native app 的编程框架. 推出不久便引发了广泛关注, 这也得益于 Java ...

  3. 发布 Rafy 源码到 GitHub

      最近项目组开始使用 Git 来作为源码管理.我今天就顺便把 Rafy 的源码也迁移到了 github 上,方便大家使用.这是项目的地址:https://github.com/zgynhqf/raf ...

  4. [PHP]Maximum execution time of 30 seconds exceeded

    前言 在使用PHP渲染页面页面的时候,如果程序处理的时间特别久,超过配置文件(php.ini)设置的超时时间,就会出现如下提示: Maximum execution time of 30 second ...

  5. 从Insider计划看Win10的发展

    Windows 10 Insider计划是微软为了更好的倾听用户的需求而推出的用户测试项目,参与该项目的 Insider可以免费使用Windows 10 预览版.同时这些用户还需要对 Windows ...

  6. 积累一下SQL

    开篇先自我检讨一下,写了博客几年以来首次试过连续两个月没出过博文,有客观也有主观原因,但是最近这年里博文数量也越来越少,博文的质量也每况日下.希望自己一直能坚持下来,多写写博文,这月尽量多写几篇来弥补 ...

  7. 【C#公共帮助类】枚举独特类

    这个是枚举类,可能大家根据个人需求不同,不是很需要,但是跟着做那个项目的朋友会用到 我在这贴一下代码 using System; using System.Collections.Generic; u ...

  8. EF Core1.0 CodeFirst为Modell设置默认值!

    当我们使用CodeFirst时,有时候需要设置默认值! 如下 ; public string AdminName {get; set;} = "admin"; public boo ...

  9. Andriod小项目——在线音乐播放器

    转载自: http://blog.csdn.net/sunkes/article/details/51189189 Andriod小项目——在线音乐播放器 Android在线音乐播放器 从大一开始就已 ...

  10. overflow

    1. 隐藏x轴滚动条,垂直有滚动条: <body> <div style="width:100px;height:150px;overflow:scroll;overflo ...