文章目录
- JVM垃圾回收机制
- 如何确定该对象是垃圾
- 引用计数
- 可达性分析
- 如何释放对象
- 常用策略
JVM垃圾回收机制
以对象为单位来进行回收
如何确定该对象是垃圾
Java 中使用 可达性分析方法
Python 中时使用 引用计数方法
引用计数
使用额外的计数器,来记录某个对象有多少个引用指向它(如果没有引用指向它了,它就相当于垃圾了)
-
在多线程的情况下,可能存在多个线程修改同一个变量的情况
-
如果当前类的大小比较大时,多一个引用计数没啥;但如果但钱类很小,在引入一个变量来计数,就会有很大的空间开销
-
存在循环引用的问题
可达性分析
以代码中的一些特殊的变量作为起点,然后以起点触发,看看哪些对象都能被访问到,只要对象能被访问到,就被标记为可达【借助二叉树:如果是节点就可到达,如果连接节点的线断了,就相当于不可达了】,当完成一圈标记后,剩下没有标记的就是不可达,就是垃圾
起点称为GCRoot
- 局部变量表中的引用【局部变量】,所有的局部变量都可以视为是 GCRoot
- 常量池中对应的对象
- 方法区中,静态引用类型的成员
如何释放对象
常用策略
标记-清除
先通过可达性分析,找到垃圾,然后释放【确实把内存释放了,但是出现了很多内存碎片;因为 new 对象分配的空间都是需要连续的】
复制
为了解决上述中的内存碎片问题,引入了复制算法
将空间一分为二,每次只用一半空间,但进行可达性分析后,将可达的数据进行复制到另一半空间中,然后整体释放之前占用的一半空间,就达到了空间连续
问题:直接相当于少了一半空间
标记-整理
类似于顺序表删除元素【耗时】
分代回收
将上述的算法进行综合使用,扬长避短
给对象引入了一个概念,年龄,这里的年龄指的是对象活过 GC 的轮次;对象刚创建出啦,未被 GC 洗礼过,年龄就是0,洗礼过一次就 + 1,根据年龄不同,被分为新生代和老年代
- 新创建的对象都放在伊甸区中
- 伊甸区中的对象绝大部分都活不过一轮GC,活过一轮 GC 的就放到幸存区【幸存区中的对象不会很多,所以不需要很大的空间】,从伊甸区到幸存区就是采用复制算法
- 幸存区的对象又会经过一轮又一轮的 GC 洗礼,每次都会淘汰一部分,未被淘汰的对象就被放入另一个幸存区中【复制算法】
- 当在幸存区中,很多轮的 GC 洗礼后,对象依旧存在,就将其复制到老年代中
- 对象在老年代中,依然要经历 GC 洗礼,但是周期性就可以缩短了【这就可以采用 标记-整理算法了,因为回收的次数减少了】
特例
如果当前的对象占用空间特别大,就直接将其放入老年代【大对象在复制算法中,不怎么友好】