JVM规范说了并不需要必须回收方法区,不具有普遍性,永久代使用的是JVM之外的内存
引用计数:效率要比可达性分析要强,随时发现,随时回收,实现简单,但是可能存在内存泄漏
局部变量表,静态引用变量
就是只是进行新生代回收的时候老年代的引用也可以作为GCROOTS
public class Test { public static Test obj;//这是一个类变量 // @Override // protected void finalize() throws Throwable { // System.out.println("调用当前链上的finalize方法"); // obj=this;//当前带回收的对象在finalize方法上和一个引用链上面的对象建立了联系 // } public static void main(String[] args) throws InterruptedException { obj=new Test(); //对象第一次拯救自己 obj=null; System.gc();//调用垃圾回收器 System.out.println("第一次GC"); //因为finalizer线程优先级很低,暂停2s来等待他 Thread.sleep(3000); if(obj==null){ System.out.println("对象已经死了"); }else{ System.out.println("对象还活着"); } obj=null; System.gc();//调用垃圾回收器 System.out.println("第二次GC"); //因为finalizer线程优先级很低,暂停2s来等待他 Thread.sleep(3000); if(obj==null){ System.out.println("对象已经死了"); }else{ System.out.println("对象还活着"); } } }
垃圾回收算法:
标记是非垃圾的对象就是可达的对象,然后清除清楚的是垃圾对象,要先递归进行遍历所有可达对象,然后清除的时候需要再开始遍历一遍,还需要进行维护空闲列表
就比如说我们的硬盘,只要你不小心点击了格式化,此时也不是真正的进行格式化,只是标记性删除,但是千万不要再向里面存放数据,因为数据会覆盖,就不好恢复了
复制算法:内存利用率贼低
首先经过可达性分析在A区找到可达的对象,一旦找到了可达的对象就不需要进行标记,直接将可达的对象进行复制算法放到另一块区域B,这是另一块空间的所有区域B的对象都是连续的
缺点:维护引用和对象的地址映射
回收的对象比较少,存活的对象比较多,那么移动的对象比较多,但是还要大量维护指针和对象的关系,老年代不适合使用复制算法,因为很多对象都不死,老年代复制对象开销太大
标记整理算法:
还要移动位置,还要修改引用对象关系很麻烦,这个算法比标记清除算法效率还低
分代回收:
新生代:老年代=1:2,edin区:幸存者1区:幸存者2区:
标记的开销和存活的对象成正比,因为标记只能标记存活的对象
清除阶段要进行全堆空间线性的遍历
压缩或者是整理和存活对象的大小成正比
stop the world停止的是用户线程,就是为保证一致性
可达性分析算法中枚举根节点会导致所有Java执行线程停顿
衡量一个垃圾回收器的标准就是吞吐量和低延迟