这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战
WangScaler: 一个用心创作的作者。
声明:才疏学浅,如有错误,恳请指正。
我们java程序得以顺利的执行,GC功不可没。我们知道内存是有限的,我们在程序中创建的对象都会占用资源,而资源的释放就是通过GC垃圾回收来实现的。
判断对象是否可被回收
我们知道我们的对象没有被引用时,就可以被回收,那么我们怎么判断对象是否被引用呢?
- 引用计数法:给对象增加一个引用计数器,每当有引用的时候,计数器加一;引用失效的时候,计数器减一。当计数器为0的时候,也就是无人引用的时候,将会将该对象回收。不过这种方法很少使用,因为它无法解决相互引用的问题。
- 可达性分析:通过GC-Root向下搜索,不在链路上的对象将会被回收。在链路上的即可达,则认为是存活的对象。
可作为GC-Root的对象有:
- 1、虚拟机栈中引用的对象。
- 2、方法区中类静态属性引用的对象。
- 3、方法区中常量引用的对象。
- 4、本地方法栈中JNI(Native方法)引用的对象。
垃圾回收算法
这些算法都有各自的优势和劣势,所以应当使用在各自的应用场景,分代收集法由此而来。
- 复制法:空间分为两个半区From和To,每次回收时都会将一个半区存活的对象复制到另一个半区,然后将整个半区进行回收。
- 标记清除法:先标记要回收的对象,然后统一回收。会导致内存碎片,即内存区不连续。
- 标记压缩法:扫描标记存活的对象向一端移动,从而重新组成一片连续的内存区域。需要移动内存对象的成本。
- 分代收集法:新生代(大批对象死去、少量对象存活)使用复制算法,复制成本低;老年代(对象存活率高、没有额外空间进行分配担保的),采用标记-清理算法或者标记-整理算法。
复制法
GC整体如上图所示,分为新生代和老年代。
- 新生代:新生代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。分为Eden区和两个Survivor区。
- 老年代:年老代主要存放JVM认为生命周期比较长的对象(在新生代经过几次GC之后还存活的对象)。
接下来,我们就来看看分代收集机制。
1、当对象创建的时候,就会将对象放进Eden区,直到Eden区满之后,就会触发GC,此时将Eden区中存活的对象通过复制算法复制到From区。
此时将剩余的Eden区的对象进行回收。
2、当下一次Eden区满之后触发GC时,会将Eden区和From区存活的对象,复制到To区。将Eden区和From区剩余的对象进行回收。
3、当下一次Eden区满之后再次触发GC,此时将Eden区和To区存活的对象,复制到From区。然后将Eden区和To区剩余的对象进行回收。
4、上述的2、3步骤循环往复,直至From区或者To区到达阈值。此时在From区和To区来回游荡还一直存活的对象,将被复制到老年代。
5、当老年代也到达阈值的时候,就会采用标记-清理算法或者标记-整理算法进行全量回收。此时除了垃圾回收的线程以外,其余的线程都将停止。
标记清除法
如上图所示,红色表示存活的对象,蓝色表示有引用,但不是GC-Roots可达的对象,而灰色的表示已经不是存活的对象。那么除了红色存活的对象被标记之后不会被回收,其他的都将被回收。
标记压缩法
标记压缩法则是将存活的对象移动到内存边界,然后将剩下的空间进行回收。
总结
以上三种方法结合起来就是分代收集法,今天的分享就到这里。
来都来了,点个赞再走呗!
关注WangScaler,祝你升职、加薪、不提桶!