JVM通过GC回收堆和方法区的内存,这个过程是自动执行的。我们需要考虑三件事
一、哪些内存需要回收
二、什么时候需要执行GC
三、如何执行GC
一、判断垃圾回收的对象
1.1计数收集器
当计数器为0时,此对象不再被引用可以回收。
ObjectA释放了对ObjectB的引用后,ObjectB的引用计数器变为0,此时可回收ObjectB所占有的内存。
1.2跟踪收集器
采用的为集中式的管理方式,会全局记录数据引用的状态。有三种实现方式:复制,标记-清除,标记-清除-压缩
二、什么时候需要执行GC
如果只是采用标记-清除-压缩的方式回收,那么随着对象的增加扫描和移动消耗的时间会越来越久,内存回收越来越慢,显然这样是不能满足我们的需求的,所以JVM采用分代回收的方式,将堆区分为年轻代和老年代。
1.在初始阶段新建对象会被分配到Eden区,survivor的区间是空的
2.当Eden区满的时候,minor gc会被触发,经扫描和标记存活的对象被复制到S0,不存活的对象被回收
3.第二次Minor GC被触发的时候,S0的数据被复制到S1中,其中对象的年龄加1,Eden中存活的数据也被复制到S1中,S0和Eden被清空,这样S1就存放着年龄不一样的对象
4.下一次Minor GC则重复这个过程,每次S0 和S1两个区对换
5.再经过几次Minor GC之后,当存活对象的年龄达到一个阈值之后(可通过参数配置,默认是8),就会被从年轻代Promotion到老年代。
6.随着MinorGC一次又一次的进行,不断会有新的对象被promote到老年代。 最终,MajorGC将会在老年代发生,老年代的空间将会被清除和压缩。
什么样的对象可以进入老年代???
1)在年轻代中如果一个对象年龄达到阈值,就会移动
2)Survivor中相同年龄的对象大小超过survivor空间一般,那么不小于这个年龄的对象会进入老年代
3)创建的对象的大小超过阈值,这个对象会直接进入老年代
4)年轻代中大于survivor空间的对象,会移动到老年代
三、如何执行GC
复制
将根集合中回收后依然存活的对象复制到一块新的完全未被使用的空间
标记-清除
将根集合中存活的对象标记,清除未标记的对象,在这个过程中不需要进行对象的移动,会造成内存碎片
标记-压缩
与标记清除类似,只是在处理结束后会将存活对象内存移动,将内存碎片集中在一起使用
四、小结
Eden区是连续的空间,S0和S1总有一个为空,S0和S1总是进行角色的交换,这种回收方式是“停止-复制”清理发,对于大部分对象存活周期很短的情况下比较适用。而老年代采用的方式是标记-压缩算法。如果年轻代过小,容易导致GC频繁地发生,增大系统消耗,这样很多大文件会直接进入老年代,容易诱发Full GC,如果老年代过小,更容易诱发Full GC。
综上:1.对象会优先在Eden上分配
2.大对象直接进入老年代
3.长期存活的对象进入老年代
本篇博客主要用于知识的总结,是模仿该博客写的,大家可以区查看原博主,写的非常详细
https://blog.csdn.net/suifeng3051/article/details/48292193