• 内存回收的概念

当一个对象不再被引用时,原本分配给此对象的内存便成为垃圾,JVM的一个系统级线程会自动释放该内存块

在此要引出“引用传递”这一概念

引用传递本质.png

堆内存栈内存指向.png

此时若执行   per2 = per1; 内存指向就发生了新的变化:

堆内存的垃圾空间.png


当一个对象的引用被置为null时,垃圾回收器并不会立即回收,而是在下一次进行垃圾回收时释放其所占内存

垃圾回收机制只针对堆内存中的对象:

堆内存

堆内存的构造.png


JVM虚拟机的垃圾回收机制有以下优点

  1. 提高编程效率

  2. 保护程序完整性

缺点

  1. 垃圾回收的开销影响程序性能(要实现垃圾回收,Java虚拟机必须追踪运行程序中有用的对象,而最终释放没用的对象。这一过程需要花费处理器的时间)

  2. 垃圾回收算法不完备性

  • 垃圾回收(GC)算法

  1. 发现无用信息对象

  2. 回收被无用对象占用的内存空间,使该空间可以被程序再次使用

(Java语言规范没有明确说明JVM使用哪种垃圾回收算法)

大部分垃圾回收算法都使用了“根集”这一概念

“根集”:正在执行的Java程序可以访问的引用变量的集合(局部变量,参数,类变量)

(1)从根开始可达的对象都是活动对象,它们不可作为垃圾进行回收

(2)从根开始任意路径不可达的对象符合垃圾回收的条件,,可以作为垃圾进行回收

  • 常用算法:

1.标记—清除算法

思想:首先标记出需要回收的对象,在标记完成后同一回收掉所有被标记的对象

缺点

(1)标记和清除效率都比较低

(2)标记清除后会产生大量的不连续的内存碎片

2.复制算法

思想:将可用的内存空间按容量平均分为两块,每次只使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另一块上面,然后再把使用过的内存空间一次性清理掉。

缺点:将内存缩小到了原来的一半,持续复制存活率高的对象会导致效率降低

3.标记—压缩算法

思想:根据老年代的特点,标记过程与“标记—清除”算法相同,然后让所有存活的对象都向一端移动,再直接清除掉端边界以外的内存。

4.分代收集算法

思想:把Java堆分为新生代和老年代,新生代中每次垃圾收集时只有少量存活,一次使用“复制算法”,老年代使用“标记—压缩算法”


  • GC收集器

1.Serial收集器(复制算法)

串行收集器,只使用一个线程去回收

新生代,老年代使用串行回收

2.ParNew收集器(停止—复制算法)

多线程回收

新生代并行,老年代串行

3.Parallel收集器(停止—复制算法)

动态调整以提供最合适的停顿时间和最大吞吐量

Parallel收集器+老年代串行

4.Parallel Old收集器Parallel收集器(停止—复制算法)

老年代版本,使用多线程和“标记—清除”算法

Parallel收集器+老年代并行

5.CMS收集器

获取最短回收停顿时间,响应速度快(服务端),使用“标记—清除”算法

(1)初始标记:标记根集直接关联到的对象(时间短)

(2)并发标记:标记根集(时间长)

(3)重新标记:修正并发标记期间因用户程序继续运作,而导致标记产生变动的对象的标记记录(时间较短)

(4)并发清除:清除标记对象(时间长)

老年代收集器,新生代使用ParNew

优点

并发收集,停顿低

缺点

产生大量空间碎片,并发阶段降低吞吐量

6.GI收集器

最新

特点:

(1)空间整合,不会产生内存空间碎片

(2)可预测停顿,降低停顿且耗在垃圾收集上的时间不得超过N毫秒

(3)收集范围不再只是新生代和老年代

注意:Java有了GC同样会出现内存泄漏问题

程序员不需要创建线程来释放内存,也不允许程序员直接释放内存,内存回收程序不可以在指定的时间释放内存,不一定在什么时候进行垃圾回收


  • 垃圾回收中所用到的方法

在垃圾回收任何对象之前都会调用finalize()方法,该方法可能会使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消回收

finalize()方法:

  1. 不要主动调用该方法,留给垃圾回收机制调用

  2. 何时被调用,是否被调用具有不确定性

  3. 可使可恢复状态的对象变为可达状态

  4. 当JVM执行该方法出现异常时程序继续执行,不会报告异常

    对象的状态转换.png


强制系统垃圾回收(通知)

  1. System.gc();//静态方法

  2. Runtime.getRuntime().gc()//Runtime对象的实例方法