前两篇讲了Java运行时内存的各个区域。对于程序计数器、虚拟机栈、本地方法栈这三部分区域而言,其生命周期与相关线程有关,随线程而生,随线程而灭。并且这三个区域的内存分配与回收具有确定性,因为当方法结束或者线程结束时,内存就自然跟着线程回收了。
Java堆中存放着几乎所有的对象实例,垃圾回收器在对堆进行垃圾回收前,首先要判断这些对象哪些还存活,哪些已经"死去"。判断对象是否已"死"有如下几种算法
① 死亡对象的判断算法
a) 引用计数算法—其他大部分语言使用
b) 可达性分析算法—JAVA中使用
从—组初始的位置出发向下进行深度遍历,把所有能够访问到的对象都标记成“可达"(可以被访问到).对应的,不可达的对象(没有标记的对象)就是垃圾~
JVM中采取的方案.在JVM中就存在一个/一组线程,来周期性的,进行上述遍历过程~~不断的找出这些不可达的对象.由JMM进行回收~~
不管是引用计数,还是可达性分析,判定原则,其实都是看当前这个对象是否有引用来指向~~是在通过引用来决定对象的生死~
把可达性分析,初始位置称为"GCRoot",一般有三种会可能。
1.栈上的局部变量表中的引用~
2常量池里面的引用指向的对象
3.方法区中,引用类型的静态成员变量~
和引用计数相比,可达性分析,确实要更麻烦,同时实现可达性分析遍历过程也是开销比较大的.
但是带来的好处就是解决了引用计数的两个缺点:内存上不需要消耗额外的空间~,也没有循环引用的问题~
垃圾回收中的经典的算法/策略~~
a)标记–清除
为了解决这里的内存碎片,解决方案,就是复制算法~~
b)复制算法
c)标记–整理
当前 JVM 垃圾收集都采用的是"分代收集(Generational Collection)"算法,这个算法并没有新思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记-清理"或者"标记-整理"算法
实际实现的垃圾回收算法,要能够结合以上的三种方式,取长补短~ 实际实现的垃圾回收算法,要能够结合以上的三种方式,取长补短~
哪些对象会进入新生代?哪些对象会进入老年代?
新生代:一般创建的对象都会进入新生代;
老年代:大对象和经历了 N 次(一般情况默认是 15 次)垃圾回收依然存活下来的对象会从新生代移动到老年代。
面试题 : 请问了解Minor GC和Full GC么,这两种GC有什么不一样吗?
1. Minor GC又称为新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。
2. Full GC 又称为 老年代GC或者Major GC : 指发生在老年代的垃圾收集。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行
Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
分代回收的过程:
1.一个新的对象,诞生于伊甸区的~~
⒉.如果活到1岁的对象(对象经历了一轮GC还没死),就拷贝到生存区~
生存区看起来比伊甸区小很多,空间能放下这么多对象吗?
根据经验规律,伊甸区的对象,绝大部分都是活不过一岁,只有少数对象能够来到生存区.对象大部分是"朝生夕死"的~
3.在生存区中,对象也要经历若干轮GC.每一轮GC逃过的对象,都通过复制算法拷贝到另外的生存区里~
这里面的对象来回拷贝,每一轮都会淘汰掉一波对象~
4.在生存区中,,熬过一定轮次的GC之后,这个对象仍然屹立不倒!JVM就认为,这个对象未来还会更持久的存在下去~
(这个结论也是一个经验规律.如果一个东西存在的越久。就认为还会继续持续存在下去,要死,早死了)就好比C语言已经存在50年了,我们认为C至少还能再存在50年于是这样的对象就把它拷贝到老年代~~
5.进入老年代的对象,JVM都认为是属于能持久存在的对象.这些对象也需要使用GC来扫描.但是扫描的频次就大大降低了~~老年代这里通常使用的是标记整理算法~~