做JAVA就是爽,不用手动垃圾回收,JVM里帮我们写好了垃圾回收器,自动帮我们清理程序运行过程中产生的垃圾对象,保证内存的重复使用!
但是!咱们还是要了解一下的。一方面扩展自己的知识点,垃圾回收机制设计的确实牛逼。一方面,面试官容易问!好吧,我承认后者才是我学习的重点。
首先,我们都知道 程序运行中,内存里 有 堆、栈、方法区等。那么GC主要是处理堆的,
堆中的内存分布,分为了三大块,新生代,老年代 ,元空间(1.8之前叫永久代,1.8及以后废弃了并增加了元空间)。本文重点讲 新生代和老年代这块。元空间可自行查阅相关资料。
新生代中又划分出了三块,伊甸园区和两个幸存者区。如下图:
新生代区域执行的GC叫 Minor GC,老年代执行的GC叫 FULL GC管理,可以理解为 坐飞机 头等舱和经济舱的服务是不一样的。当然目的相同都是服务好顾客。
Minor GC执行速度很快。采用了复制算法 即把伊甸园中幸存的对象复制到幸存者区域中,复制完毕后,伊甸园区全部清空。优点,速度快!缺点,需要额外的空间用于存储复制后的对象!一个1MB的对象要完成本次操作要使用2MB的空间,
FULL GC 执行慢,采用了标记清除+压缩算法,执行时先扫描一遍,对有引用的对象做标记。第一轮扫描后,第二轮扫描开始,清除未被标记的对象。标记清除 执行完毕后,会产生一些内存碎片,如下图,红色区域就是刚刚删除的对象所占空间!
当 标记清除 执行多次后,会产生大量的内存碎片,这时候要执行压缩算法。把零零散散的对象在内存中压缩到一块,放到一起,否则来个大对象插不进去怎么办?如下图:
这样就能腾出大块的内存供程序继续使用了!
接下来看一下流程。
1.首先,程序中创建了一个对象,这时候对象要存放到堆中吧。先检查伊甸园区是否可以存放,可以存放直接存放。完毕!
不能存放就需要执行一次 Minor GC ,采用的是复制算法,把伊甸园区的有用对象复制到幸存者区,无用的就删除掉。这样就腾出了伊甸园区,再尝试把对象放进来,能放进来正好,不能放进来说明是个大对象,
直接存放到老年代,能存放最好,不能存放则直接执行一次 FULL GC 清理老年代!清理完再次尝试是否可存放,不能存放报内存溢出。
2.说说幸存者区 ,幸存者区有两个,一个是当前存放伊甸园过来的幸存者的。另一个是用于下次GC存放幸存者的!当下次GC来临,伊甸园区和当前幸存者区的 幸存者会被安排到 另一块幸存者区域。这是为了减少碎片空间。
试想一下,如果只有一个幸存者区的话,那么一次Minor GC后 势必会形成内存碎片,难不成要把后来伊甸园存活的塞到幸存者的缝隙中?所以为了解决碎片就把幸存者和伊甸园幸存者移步到另一块幸存者营地,下次gc再移动回来!保证新生代永远没有内存碎片!
当有幸存者经历了多次大清理而依然存活。则会被安置到老年代中!
作者小菜鸡,如有理解不对,还望各位大佬指正!