垃圾收集器与内存分配策略(一)

垃圾收集器与内存分配策略之--对象已死吗

GC关注的问题其实就是三个:

  • 哪些内存可以被回收
  • 何时回收
  • 如何回收

所有的GC问题都是关于这三点的描述。
对于JVM来说,首先线程独有的三块(程序计数器、栈、本地方法栈)都是随着线程的开始而创建,线程结束而消亡,一个栈帧在开始时内存已经固定,所以GC没必要处理这部分内存。我们所说的GC都是针对堆和方法区而言的。

对象已死吗

如何判断一个对象已死?一般主流两种方法:引用计数法和可达性分析法

引用计数法

很简单,每个对象维护一个引用计数器,当某个引用指向这个对象时,就让这个引用计数器加1。当一个对象的引用计数器为0,说明对象已死。
致命的缺点:
不好解决循环引用问题。

可达性分析法

通过一组“GC Root”的对象作为起始点,向下搜索,走过的路径上的每个对象是还活着的。不可达的对象就是死的。HotSpot用的就是这个方法。
Java中可被作为GC Root的对象有四种:

  • 栈中引用指向的对象
  • 方法区中static引用指向的对象
  • 方法区中常量引用指向的对象
  • 本地方法栈中引用指向的对象

再谈引用

一般我们说的引用都是指强引用。

Object obj = new Object();//obj就是一个强引用

而Java中引入一些“弱”的引用。目的为了引入一种这样的对象:当内存足够时,对象保留内存中,当内存不够时,这些对象可被回收。
Java里引用分为四种:强引用、软引用、弱引用、虚引用。引用强度逐渐减弱。
这四种引用的区别就是指向的对象的生存时间不一样。

  • 强引用
    这个就是我们普通说的引用,如果一个对象存在这种引用。即时内存要溢出,这些对象也不能被回收。
  • 软引用
    在要发生内存溢出之前,会先将这些引用指向的对象回收调。(其中这种说法不对,应该是一个对象只有软引用指向它时)。
    比如我们定义一个软引用:
SoftReference s = new SoftReference(new String("123"));
  • 弱引用
    生命周期为下一次GC之前。无论内存是否足够,这些指向的对象都会被回收。
WeakReference w = new WeakReference(new String("234"));
  • 虚引用
    一个对象是否有虚引用和它的生命周期毫无关系。虚引用的唯一目的就是在对象被回收的时候会受到一个系统通知。
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference p = new PhantomReference<>(new String("434"), referenceQueue);//当对象被回收时,referenceQueue就会受到一个消息

一般软引用和弱引用都用于本地缓存中,每次用时判断一下是否被回收,如果被回收再去到数据库里捞。

SoftReference s = new SoftReference(new String("123"));
if (null == s.get()) {//如果已被回收
    //到数据库里捞
}else {//如果没被回收
    System.out.println(s.get());//直接使用
}

生存还是死亡

一个对象GC Root不可达并不代表着立刻死亡。一个对象的死亡要经历两次标记。
具体过程如下:


垃圾收集器与内存分配策略(一)_第1张图片
对象死亡过程
  • 当GC Root不可达之后被标记一次,
  • 然后判断这个对象是否需要执行finalize方法。
    判断不需要的标准是:对象没有重写finalize方法或finalize已被虚拟机执行过。[也就是说对象finalize只能被执行一次]
  • 如果判断需要执行,则将这个对象丢到F-Queue中,然后虚拟机会有一个Finalize线程去消费执行对象的finalize方法,但并不会等待执行结束。如果在执行finalize方法中[在被标记第二次之前]又把此对象关联到GC Root上,这个对象就又会活过来。【注意这种现象一个对象只能出现一次,因为finalize方法只可能会执行一次】
  • 所以说一个对象的finalize被执行了,这个对象可能还是活着的。且finalize方法被强烈建议不要使用,这个方法不会有人保证它是否会执行、何时执行、何时执行结束。

回收方法区

回收方法区的性价比很低,且Java虚拟机规范中并没有强制要求要回收方法区。
回头方法区主要回收两点:常量和无用的类。

  • 常量
    这里说的就是常量池中常量,比如字面量和符号引用。
    判断是否应该回收比较简单,比如字面量:系统中没人叫这个东西了,自然就可以被回收了。
  • 无用的类
    这个判断起来就比较麻烦了。
    主要由三点:
    该类的实例已全部被回收
    加载该类的ClassLoader已被回收
    该类的Class对象无引用且无法使用反射来调用
    满足这三点是可以被回收,但也不是一定就会被回收。可以使用-Xnoclassgc来控制。

你可能感兴趣的:(垃圾收集器与内存分配策略(一))