内存回收的概念
当一个对象不再被引用时,原本分配给此对象的内存便成为垃圾,JVM的一个系统级线程会自动释放该内存块
在此要引出“引用传递”这一概念
此时若执行 per2 = per1; 内存指向就发生了新的变化:
当一个对象的引用被置为null时,垃圾回收器并不会立即回收,而是在下一次进行垃圾回收时释放其所占内存
垃圾回收机制只针对堆内存中的对象:
堆内存
JVM虚拟机的垃圾回收机制有以下优点:
提高编程效率
保护程序完整性
缺点:
垃圾回收的开销影响程序性能(要实现垃圾回收,Java虚拟机必须追踪运行程序中有用的对象,而最终释放没用的对象。这一过程需要花费处理器的时间)
垃圾回收算法不完备性
垃圾回收(GC)算法
发现无用信息对象
回收被无用对象占用的内存空间,使该空间可以被程序再次使用
(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()方法:
不要主动调用该方法,留给垃圾回收机制调用
何时被调用,是否被调用具有不确定性
可使可恢复状态的对象变为可达状态
当JVM执行该方法出现异常时程序继续执行,不会报告异常
强制系统垃圾回收(通知)
System.gc();//静态方法
Runtime.getRuntime().gc()//Runtime对象的实例方法