垃圾回收机制可以用3个词概括 where when how
where:运行时内存分布情况:-------------------------------------------------------------------
运行时数据区域
方法区 虚拟机栈 本地方法栈
堆 程序计数器
----------------------------------------------------------------------
程序计数器:是一块较小的内存,可以当作当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时通过改变这个 计数器的值来选取下一条需要执行的字节码指令。JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的。
JVM stack 虚拟机栈 :是线程私有的,它的生命周期与线程相同,JVM栈描述的是java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(stack frame) 用于存储 局部变量表 操作数栈 动态链接 方法出口等信息。 每个从调用直至执行完成的过程 就对应着一个栈帧在 虚拟机栈中入栈到出栈的过程。
本地方法栈:虚拟机栈为虚拟机执行java方法服务 而 本地方法栈为虚拟机使用到的native方法服务。
java堆:是java虚拟机所管理的内存最大的一块,java堆是被所以线程共享的一块内存区域 ,存放的是对象的实例 。还可以细分为 :新生代 老生代。2还可分为
Eden空间 from survivor空间 to survivor空间。
方法区 :是各个线程共享的一块内存区域,用于存储已被虚拟机加载的 类信息 常量 静态变量 即使编译器编译后的代码等数据。
(运行时常量池:是方法区的一部分,class文件中除了有类的版本 字段 方法 接口等描述信息 还有常量池 用于存放编译期生成的各种字面量 和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。)
when:对象何时需要被回收?何时i回收无效对象?已死对象? 引用计数器法 和可达性分析算法
引用计数器法 : 给对象添加一个引用计数器,每当一个地方引用它时,计数器值+1,当引用失效时就-1,任何时刻计数器值为0的对象就是 不可能再被使用的。 缺点 :很难解决对象之间相互循环引用的问题。
objA.instance=objB及 objB.instance= objA
可达性分析算法:通过一系列称为“GC Roots”的对象作为起始点 从这个结点开始向下搜索 搜索所走过的路径称为 引用链 。当一个对象到GC Roots没有任何引用链相连时 则说明此对象是不可用的。
GC Roots对象包括:JVM 栈:(栈帧中的本地方法表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(native方法)引用的对象
how 对象是如何被回收的?
标记--清除 算法:(Mark--sweep)首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象
缺点:1 效率:标记和清除2个过程的效率都不高,另外一个是标记清除后会产生大量不连续的内存碎片 会导致以后在程序运行过程中 需要分配较大对象时 无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
复制算法:它将可用的内存按容量分为大小相等的2块 每次只使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。 (回收 新生代)
标记--整理算法:(回收老年代)过程和“标记--清除 算法”一样 后续步骤是不是对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法:根据对象存活周期的不同 将内存划分为几块 一般是把java堆分为 新生代和老年代 。这样就可以根据各个年代的特点采用最适当的收集算法,
在新生代,每次垃圾收集都发现有大批对象死去,只有少量存活 选用复制算法。而老年代中因为对象存活率高,没有额外空间对它进行分配担保,使用标记--清理 或者 标记--整理算法来进行回收。