gc垃圾回收图解

目录:
java虚拟机汇总

  1. class文件结构分析
    1).class文件常量池中的常量项结构
    2). 常用的属性表的集合
  2. 类加载过程
    1).类加载器的原理以及实现
  3. 虚拟机结构分析
    1).jdk1.7和1.8版本的方法区构造变化
    2).常量池简单区分
  4. 对象结构分析
    1).压缩指针详解
  5. gc垃圾回收<<== 现在位置
  6. 对象的定位方式

什么是垃圾?

我们创建出来的对象没有引用指向的的时候,这个对象就再也无法被用到,就是说,这个对象已经成为了垃圾对象,一直存放在我们的内存当中
例:
Student s =new Student();
s = null;

我们怎么判断一个对象是不是垃圾

两种方式

  1. 引用计数法
    这种方式根据这个对象是否有引用指向,
    例:
    Student s =new Student(); //这样这个Student对象的引用数为1
    Student s2 = s; //此时这个对象引用数为2
    当一个对象的引用为0时,判定为垃圾
    问题:
    如果有 下面所示代码 A中对象有一个属性指向B,而B中也有以一个属性指向A,那么这两个对象的引用数都不会为0,但是事实是这两个对象都不可能在被用到了,是垃圾
class TestB{
    
    

   public TestA testa;

    public TestB(TestA testa) {
    
    
        this.testa = testa;
    }
    
    public static void main(String[] args) {
    
    
        TestA testa = new TestA();
        TestB testb = new TestB(testa);
        testa.testb = testb;
        testa = null;
        testb = null;
    }
}
public class TestA {
    
    
    public TestB testb;

    public void setB(TestB b) {
    
    
        this.testb = testb;
    }
}

  1. 可达性分析法
    第二种判断对象是否是垃圾的方法,GCRoot根搜索法
    这种方式从一个GCRoot根出发,所有依赖到的对象都被标记为不是垃圾,其他的都是垃圾
    GCRoot根:
    1. 堆中类静态属性引用的对象
    2. 方法区中变量引用的对象
    3. 方法区中常量引用的对象
    4. 虚拟机栈中本地变量表引用的对象
    5. 本地方法栈引用的对象(就是native方法里面引用的对象)
    看着很蒙?不要紧
    举个例子(看注释)
class TestB{
    
    

   static TestA a1= new TestA(); //对应上面的第一个
   final TestA a2 = new TestA();  //对应上面第二个
   TestA a3 = new TestA();//对应上面第三个
  
   public void method1(){
    
    
       TestA a = new TestA(); //对应上面的第四个
   }
}

怎么回收垃圾

知道了哪些是垃圾,我们考虑怎么清除这些垃圾

  1. 复制算法
    空间一分为二,一边放对象,另一边什么都不放,空着
    在这里插入图片描述
    开始清理垃圾,将存活对象复制另一部分,直接把原来的空间清空,然后右边空间再作为对象存放的地方,左边则成为什么也不放的区域,空着
    在这里插入图片描述
    清除效率高,适合一个区域垃圾很多,存活的很少,这样我们只需把存活的几个移动一下就行了,但如果垃圾很少,存活的很多,那我们就需要复制一大堆存活对象到另一个存储空间,效率低,而且这种方式很费空间,毕竟只用一半

  2. 标记清除
    很简单就是把所有的垃圾标记一遍,直接清除所有的垃圾
    在这里插入图片描述
    在这里插入图片描述
    这种方式,会产生很多内存碎片,其他大对象来的时候存不进去,效率适中

  3. 标记整理
    在上面的基础上,清除以后整理一遍,使得对象排列规整
    在这里插入图片描述
    这种方式没有碎片但是,又要标记又要清除又要移动到指定区域,等于重新复制了一遍,所以效率不高

hostSpot虚拟机堆中的垃圾回收

了解完了怎么样判断是否是垃圾,和垃圾怎么回收,看看我们虚拟机里用了哪些方法

我们虚拟机采用了封装的技术,封装了几个垃圾收集器,用来回收垃圾,接下来分析每个垃圾收集器的讲解
1.分代垃圾回收器
在这里插入图片描述
在这里插入图片描述
先说下我们jdk1.8默认使用的是ps+po来处理的,就是图中的Parallel Scavenge和Parallel Old

在这里插入图片描述
这样我们先了解下Parallel这一对组合
Parallel 开头的都是多线程执行
年青代
在年轻代因为对象大多都是朝生夕死的,所以很多都是垃圾,存活的只有很少,所以Parallel Scavenge用复制算法,上图中分为Eden区和两个survive区,他们为什么不是内存劈一半呢?而是8:1:1
他是这样工作的:

  1. Eden区放对象,此时两个幸存区都没有对象,发生垃圾收集时,Eden区的存活对象全部复制到survive1区,同时将Eden区和Survive2区清空,然后再向Eden区放对象,再次垃圾回收,将Eden区和survive1区的存活的对象复制到survive2中,将survive1和Eden区清空,循环往复。。。Parallel Scavenge他是这样复制的,然后每一次的垃圾回收都会给对象头上的分代年龄(4位)加一,当到15时,将对象复制到老年代,

老年代
Parallel Old工作
它采用的是标记-整理算法,这里和我们讲的标记整理算法一样,不详细讲了

需要对每个垃圾收集器进行详细了解的,直接百度

好了我们常用的垃圾回收器讲完了
再讲一些常识
发生在年轻代的gc是youngGC 发生在老年代的是FullGC,FullGC只有当内存不足的时候会发生,这时一次会回收老年代的很多对象,释放内存,youngGC很频繁,还有这里涉及stw(stopTheWord),指在垃圾回收时其他的所有线程不能工作,也就是此时服务器会处理不了任何请求,所以尽量减少fullGC和stw的时间是GC调优的关键,

这里我们回到垃圾收集器
了解了ps+po我们在来了解其他的
在这里插入图片描述
这个也很好理解,Serial指单行的,就是只有一个线程在执行垃圾收集任务,效果肯定不如Parallel多线程垃圾收集器,具体的区别请百度
在这里插入图片描述
这两个垃圾收集器我们讲解下,ParNew就是新的多线程收集器,具体的百度,而CMS是一个跨时代的垃圾收集器,因为它内部标记垃圾的时候是和其他线程并发执行的,也就是说别的垃圾收集器在工作的时候,服务器不能相应,而CMS是并发的,也就是说在我们gc工作的时候,其他线程同样可以执行,是一个划时代的结构,(了解即可CMS里面还可能会涉及一些问题,这里就不细讲,百度)
2. 不分代垃圾收集器
在这里插入图片描述
这又是一个跨时代的垃圾收集器,因为它抛弃了传统的分代,年轻代老年代,在逻辑上不分代进行垃圾回收(了解即可,每一个垃圾收集器要讲都要讲半天,这里给出脉络,具体百度)

一些其他的小知识(面试):
什么是强引用,弱引用,软引用,虚引用,

猜你喜欢

转载自blog.csdn.net/lioncatch/article/details/106070460