GC算法和种类
GC算法和种类 GC的工作区域 1.不是GC的工作区域 程序计数器、虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭; 在这几个区域不需要过多考虑回收的问题方法结束或线程结束时,内存自然就跟随着回收了。 2.GC的工作区域(哪些内存需要GC回收?) 垃圾回收重点关注的是堆和方法区部分的内存。 程序处于运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,所以垃圾回收器所关注的主要是这部分的内存。 垃圾对象的判定 Java堆中存放着几乎所有的对象实例,垃圾收集器对堆中的对象进行回收前,要先确定这些对象是否还有用,哪些还活着。对象死去的时候才需要回收。 1. 引用计数法 在堆中存储对象时,在对象头处维护一个counter计数器,如果一个对象增加了一个引用与之相连,则将counter++。 如果一个引用关系失效则counter–。如果一个对象的counter变为0,则说明该对象已经被废弃,不处于存活状态。 2. 可达性分析算法 通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,就证明此对象是不可用的。 - 可作为GC Roots的对象包括下面几种: (1)虚拟机栈(栈帧中的本地变量表)中引用的对象。 (2)方法区中的常量、类静态属性引用的对象(静态变量)。 (3)本地方法栈中JNI(Native方法)的引用对象。
垃圾回收算法 标记清除算法 (1). 标记阶段:找到所有可访问的对象,做个标记 (2). 清除阶段:遍历堆,把未被标记的对象回收 缺点: (1)因为涉及大量的内存遍历工作,所以执行性能较低 (2)对象被清除之后,被清除的对象留下内存的空缺位置会造成内存不连续,空间浪费。
标记整理算法 适合用于存活对象较多的场合,如老年代。 (1)、标记阶段:它的第一个阶段与标记/清除算法是一模一样的。 (2)、整理阶段:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。 优点:内存连续,消除复制算法内存减半的代价问题 缺点:不仅要标记所有存活对象,还要整理所有存活对象的引用地址。性能低于复制算法。
复制算法 复制算法简单来说就是把内存一分为二,但只使用其中一份,在垃圾回收时,将正在使用的那份内存中存活的对象复制到另一份空白的内存中,最后将正在使用的内存空间的对象清除,完成垃圾回收。 优点:复制算法使得每次都只对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。 缺点:复制算法的代价是将内存缩小为原来的一半,这个太要命了。
**现在的虚拟机使用复制算法来进行新生代的内存回收。因为在新生代中绝大多数的对象都是“朝生夕亡”,所以不需要将整个内存分为两个部分,而是分为三个部分,一块为Eden(伊甸区)和两块较小的Survivor(幸存区)空间(默认比例->8:1:1)。每次使用Eden和其中的一块Survivor,垃圾回收时候将上述两块中存活的对象复制到另外一块Survivor上,同时清理上述Eden和Survivor。所以每次新生代就可以使用90%的内存。只有10%的内存是浪费的。(不能保证每次新生代都少于10%的对象存活,当在垃圾回收复制时候如果一块Survivor不够时候,需要老年代来分担,大对象直接进入老年代) **
总的来讲:复制算法不适用于存活对象较多的场合,如老年代(复制算法适合做新生代的GC)
三种算法比较 相同点: 进行GC时需要暂停应用程序。 区别: 效率:复制算法>标记-整理算法>标记-清除算法; 内存整齐度:复制算法=标记-整理算法>标记-清除算法 内存利用率:标记-整理算法=标记-清除算法>复制算法
分代收集思想 新生代:由于存活的对象相对比较少,因此可以采用复制算法该算法效率比较快。 > 新生代包含Eden(伊甸区)和两块Survivor(幸存区)
老年代:由于存活的对象比较多哈,可以采用标记-清除算法或是标记-整理算法。
java8以后,已经没有(PermGen Space)永久区了,之前永久区存放的东西基本上放到了元空间(Meta Space)中。
参考:https://www.……