JAVA垃圾回收机制基本概念
JVM垃圾回收基本策略
背景知识介绍
JVM
百度百科上是这么介绍的:
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
JAVA的内存模型(JMM)
计算机程序在运行的过程中都需要同内存打交道,JAVA内存模型就是JAVA虚拟机在同内存的交互过程中,对他所管理的内存进行的区域划分、分配不同的职责,协同工作,更加高效、便捷的完成程序的运行。简单来说可以大至分为以下几个区域。
其中虚拟机栈、程序计数器、本地方法栈是线程私有的。堆、方法区是所有线程共有的。
- 程序计数器
一块较小的空间,其作用可以看做当前线程所执行的字节码的行号指示器。
- 虚拟机栈
虚拟机栈描述的是Java 方法执行的内存模型
- 本地方法栈
与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。
- 堆
Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
- 方法区
是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
什么是垃圾?
在理解垃圾收集器前,需要先明白是什么是垃圾?内存中的垃圾是什么?
垃圾在java虚拟机中就可以理解为是没有引用的对象,因为没有引用的对象是无法去使用它的。所以就可以认为这个对象是垃圾,需要被回收释放其占用的内存空间。
怎么确认什么是可以被回收的对象呢?
可达性检测
- 引用计数法
一种在jdk1.2之前被使用的垃圾收集算法,原理是对象有一个引用,就增加一个计数,删除一个引用,则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。
- 根搜算法
思想是从GC ROOT根引用关系来遍历整个堆并作标记,最后未被标记的所有对象,就是可以被回收的垃圾。
可以被做为根的对象:
a.虚拟机栈(栈桢中的本地变量表)中的引用的对象
b.方法区中的类静态属性引用的对象
c.方法区中的常量引用的对象
d.本地方法栈中JNI的引用的对象
整理策略
- 标记-清除(Mark-Sweep):
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。
- 复制(Copying):
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
- 标记-整理(Mark-Compact):
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
分代收集(Generational Collecting):
基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。
串行收集:
串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。
并行收集:
并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。
并发收集:
相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。