你的位置:首页 > Java教程

[Java教程]第三章 JVM内存回收区域+对象存活的判断+引用类型+垃圾回收线程


注意:本文主要参考自《深入理解Java虚拟机(第二版)》

说明:查看本文之前,推荐先知道JVM内存结构,见《第一章 JVM内存结构》

1、内存回收的区域

  • 堆:这是GC的主要区域
  • 方法区:回收两样东西
    • 无用的类
    • 废弃的常量
  • 栈和PC寄存器是线程私有区域,不发生GC

 

2、怎样判断对象是否存活

垃圾回收:回收掉死亡对象所占的内存。判断对象是否死亡,有两种方式:

  • 引用计数法
    • 原理:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值+1;引用失效时,计数器值-1
    • 实际中不用,不用的两个原因
      • 每次为对象赋值时,都要进行计数器值的增减,消耗较大
      • 对于A、B相互引用这种情况处理不了(这一点是不用的主要原因)
  • 可达性分析(跟踪收集)
    • 原理:从根集合(GC Roots)开始向下扫描,根集合中的节点可以到达的节点就是存活节点,根集合中的节点到达不了的节点就是将要被回收的死亡节点,如下图中的A/B/C是存活节点,D/E是死亡节点:

    • 根集合中的节点包括:
      • Java栈中的对象引用(存在于局部变量表中,注意:局部变量表中存放的是基本数据类型和对象引用)
        • 这是垃圾回收最多考虑的地方,所以有时,我们也会将死亡对象称为"没有引用指向的对象"
      • 方法区中:类静态属性的引用和常量池中的引用
      • 本地方法栈中JNI引用的对象

 

3、3种引用类型

  • 强引用(Strong Reference):A a = new A();//a是强引用
  • 软引用(Soft Reference):当内存不足时,释放软引用所引用的对象;当内存足够时,就是一个普通对象(强引用)
  • 弱引用(Weak Reference):弱引用对象只能存活到下一次垃圾回收之前,一旦发生垃圾回收,立刻被回收掉

 

4、方法区的回收

  • 废弃常量:例如,没有任何一个引用指向常量池中的"abc"字符串,则"abc"字符串被回收
  • 无用的类:满足以下三个条件
    • Java堆中不存在该类的任何实例
    • 加载该类的ClassLoader被回收
    • 该类的Class对象没有在任何地方被引用

注意:

  • 在实际开发中,尽量不用JSP去做前端,而是用velocity、freemarker这样的模板引擎去做
  • 与类相关常用的三个参数:
    • -XX:+PrintClassHistogram:输出类统计状态
    • -XX:-TraceClassLoading:打印类加载信息
    • -XX:-TraceClassUnloading:打印类卸载信息View Code

 

5、垃圾回收线程

系统的垃圾回收是由垃圾回收线程来检测操作的,该线程是一个后台线程(daemon thread)。

5.1、后台线程与我们使用的前台线程而言,有一个特点:当JVM中的前台线程数量为0时,后台线程自动消亡。可以这样讲,后台线程依托于前台线程而存在。

5.2、垃圾回收线程为什么要设置成为后台线程呢?

我们想一下,当前台一个线程都没有时,垃圾还会有吗?或者说垃圾回收还有必要吗?答案是没有必要,所以此时垃圾回收线程也就失去了存活的意义。

所以可以这样讲,将一个线程是否设置为后台线程,就看这条线程在没有其他线程存在的情况下,是否还有存活的意义。

例如,在我们使用Apache mina2做RPC时,我们在消息的接收端直接开启一个后台线程启动服务来接受消息发送端发来的消息事件请求就可以。试着去想,如果在整个JVM中只有当前的这一个后台线程了,那么这个线程还有必要存活下来吗?当然没有必要,因为消息永远都不会再发送了(前台线程都没了)