性能优化--垃圾回收器

首先需要了解JVM的内存管理,分为两大块:

  • 共享区内存:堆,方法区
  • 独占区内存:虚拟机栈、本地方法栈、程序计数器

具体的可以参考我的另外一篇文章**JVM内存管理 。**其中程序计数器是负责程序的执行操作,比如一个程序执行到第10行代码,另外一个程序开始执行。当另外一个程序完成后,就需要回到之前的第10行继续执行。这就需要程序计数器进行分配调度了。

一、垃圾标记算法

1、引用计数法

引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。比如:
性能优化--垃圾回收器
但是另外一种情况就会导致引用计数法失效:
性能优化--垃圾回收器

2、根搜索法

这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。如下图:
性能优化--垃圾回收器
其中对象1、2、3、4、5和GC root都有引用关系,所以不会被回收,对象6、7、8没有引用关系,将会被回收。

二、垃圾清除算法

1、标记清除法

算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。如下图:
性能优化--垃圾回收器
缺点:

- 需要遍历所有的内存区域,进行标记,效率低。
- 容易产生内存碎片(被清除的对象分布在不同的区域,然后申请一块比较大的内存时,有些内存区域就得不到利用,内存碎片越多,容易产生OOM)

2、复制清除法

它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
性能优化--垃圾回收器
缺点:

- 存活的对象较多时,复制效率低。(适用于新生代)
- 浪费内存,可使用的内存为原来的一半。

优点:

- 能够解决内存碎片

3、标记压缩法

在标记可回收的对象后将所有存活的对象压缩到内存的一端,使他们紧凑的排列在一起,然后对端边界以外的内存进行回收。回收后,已用和未用的内存都各自一边。如下图:
性能优化--垃圾回收器
优点:
- 解决了标记-清除算法效率低和容易产生大量内存碎片的问题,它被广泛的应用于老年代中。

4、分代收集算法

在Java虚拟机中,各种对象的生命周期会有着较大的差别,大部分对象生命周期很短暂,少部分对象生命周期很长,有的甚至和应用程序以及Java虚拟机的运行周期一样长。因此,应该对不同生命周期的对象采取不同的收集策略,根据生命周期长短将它们分别放到不同的区域,并在不同的区域采用不同的收集算法,这就是分代的概念。 如下图:
性能优化--垃圾回收器
(1)第一次,当我们创建对象时会在新生代的Eden区申请一块内存。当gc触发,发现Eden区已满(比如Eden区内存大小为10M,存活对象占用5M),那么会将存活对象存到from区域。
(2)第二次,继续创建对象,在Eden区同样申请内存,当gc到来时,发现Eden区又满了,就会把Eden区和From区的存活对象,都放到To区。并把Eden区和From区清除。
(3)第三次,此时Eden和From区都是空的。继续创建对象,Eden区又满了,就会把Eden区和To区存活的对象,都放到From区。并把Eden区和To区清空。
(4)也就是说Eden区是负责为对象开辟空间的。存活的对象会在From,To区域之间来回存放。当某个对象这样来来回回,达到一个阈值的时候,虚拟机就认定这个对象时长期存活的,就会把它放到老年代中。
(5)如果创建的对象需要的内存超过Eden大小,也会将这个对象放到老年代中。老年代内存不够时,也会进行gc。

三、对象引用类型

1、强引用

无论内存够不够,对象都不会被回收,实在不够就抛OOM异常。

2、软引用

只有内存不够的时候才会被回收。回收时间不定

3、弱引用

内存够不够都会被回收。回收时间不定

4、虚引用

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收,虚引用主要用来跟踪对象被垃圾回收的活动。回收时间不定。