JVM之垃圾收集器与内存分配策略

概述

GC主要需要完成3件事情:

  1. 哪些内存需要回收?
  2. 什么时候回收?
  3. 如何回收?

对象挂了吗?

JVM首先要知道哪些内存已经不用了,即对象已死.

引用计数算法

虚拟机不用,具体实现是:每个对象有一个引用计数器,当有地方引用它的时候,计数器就加上1,引用失效时,计数器减去1,计数器为0时表示该对象没有被引用可以被回收.

但是这样有个弊端:当对象a,b互相引用,并且a,b都应该被回收时,它们如果使用引用技术法,它们都不会被回收,导致内存泄漏.

可达性分析算法

基本思想是:先找到一个使用率最高的对象作为一个”GC Roots”,以它为起点,向下搜索,搜索所走的路径被称为引用链,不在引用链的对象都被视为已死对象,可以被GC回收.

可以作为GC Roots的对象包括如下:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI引用的对象

解释一下引用

JDK1.2之前,引用和被引用在于reference类型的数据中存储的数值是某块内存地址和内存地址被reference类型的数值所记载,这样显得特别牵强,一个对象是否对引用有点生硬.所以在JDK1.2之后给引用增加了许多的类型,分为:

  • 强引用
  • 软引用
  • 弱引用
  • 虚引用

一个对象的自我救赎

从前,有一个叫A对象的人,在人口普查中(可达性分析算法),他掉队了没有一个人认识他,他也不认识任何人.他就会被警察(GC)第一次标记,标记的条件是:他是否有finalize()免死金牌,第一次标记代表他即将要被回收掉.如果它有免死金牌它会被放入到一个F-Queue的队列中,虚拟机会让另外一个组织来(finalize线程)处理队列中的对象,这个组织是低优先级的线程,他们去检查队列中对象的免死金牌,在检查的时候如果这个对象和引用链中的对象建立了联系,那么他就会被移出死亡的队伍,他就不用死亡了.

注意

  1. finalize()方法是用来拖延死亡时间的方法,拖延的时期内需要建立连接才不死.

  2. 另外一个组织(finalize线程)是不会等待finalize()方法执行完成的(不耐烦的组织啊),为了避免finalize()方法太耗时导致队列不会停止不前.

  3. 一个对象的finalize()只会被执行一次,下次如果该对象再次陷入回收的边缘,他不会执行.

  4. 这个方法用的不多,大家可以忘记Java有这个方法(原谅我最后才说).

方法区的回收

方法区不好回收,效率比较低,分为常量的回收和无用类的回收.

常量的回收

例如字符串"abc"当没有任何一个String对象叫做"abc"时,它就会被移出常量池.

无用类的回收

需要满足三个条件:

  1. 该类的实例都已经被回收,Java堆中没有该类的任何实例.
  2. 加载类的ClassLoader已经被回收.
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射获取该类的方法.

满足以上3个条件就可以被回收了,但不一定会被回收.(不像对象一样,无用就必然被回收).

回收方法区主要用在,大量的反射,动态代理,CGLib等ByteCode框架,动态生成JSP,以及OSGi等类频繁自定义ClassLoader的场景时,需要类的卸载.

垃圾收集算法

标记-清除算法

分为标记和清除两个阶段.第一个阶段是标记,就是把已死的对象所在的内存区域进行标记,第二个阶段是清除,就是标记的内存区域的空间释放掉.

它主要有两点不足,一是:标记和清除的效率都不高,另外一个是空间的问题,标记-清除后会有大量的不连续的内存碎片,空间碎片太多会导致以后程序如果需要大块的内存区域时候,无法得到足够大小德连续内存,而触发多一次垃圾收集动作.

复制算法

为了解决效率的问题,可以使用复制算法,复制算法是先把内存分为两个相同大小的内存,一个用于存储对象,另外一个空闲着准备在垃圾收集时,先把不需要回收的内存复制到空闲内存中去,然后再把需要清除的区域完全释放干净.下次垃圾收集依次类推.

这样的算法的缺点在于,它对内存有极大的浪费,它的有效内存空间只有一半,有点太高了.

标记-整理算法

标记-整理与标记-清除类似,是先把可回收的标记起来,然后把存活对象都向一端移动,然后直接把端边界以外的内存直接清除.

分代收集算法

根据对象存活的周期不同将内存划分为几块,一般是新生代和老年代,然后根据不同的代来确定使用的收集算法.

新生代中,每次都有大量的对象死去,只有少量的存活,那么就用复制算法,只需要复制少量的内存,即可实现垃圾收集

老年代中,对象存活率比较高,那就使用标记-清理或者标记整理算法来进行回收.

HotSpot的算法实现

先不看

垃圾收集器

垃圾收集器是内存回收的具体实现.介绍7个,它们使用场景不同,作用也不同,有适用于新生代的,也后适用于老年代的,他们之间也可以配合使用.

Serial收集器

ParNew收集器

Parallel Scavenge收集器

Serial Old收集器

Parallel Old收集器

CMS收集器

G1收集器

理解GC日志

垃圾收集器参数总结

内存分配与回收策略