垃圾回收器回收哪部分内存?
关于jvm内存结构可查看另一篇随笔:https://www.cnblogs.com/shamgod-lct/p/9341567.html
程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊的执行着出栈和入栈的操作。这几个区域不用考虑内存回收的问题,方法结束或者线程结束时,内存自然跟随者回收了。
而Java堆和方法区是线程共享的,只有在程序运行期间才能知道会创建哪些对象,这部分的内存和回收都是动态的,垃圾收集器所关注的是这部分内存。
垃圾回收器回收哪些对象?
死亡的对象(不可能再被任何途径使用的对象)
怎么判断对象是否死亡?
1.引用计数法
定义:给对象中添加一个引用计数器,有一个地方引用时,计数器就加1,引用失效时,计数器就减1,任何时刻计数器为0的对象就是不可能再被使用的。
优点:实现简单、判定效率高。
缺点:很难解决对象之间相互循环引用的问题。(主流的Java虚拟机都没有选用引用计数算法来管理内存)
2.可达性分析算法
定义:通过一系列的称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象不可用。
哪些对象可以作为GC Roots对象?
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(Native 方法)引用的对象。
Java中有多少种引用?
强引用:如“Object object = new Object()”这类的引用,只要强引用还在,垃圾收集器永远不会回收。
软引用:有用但非必需的对象。在将要发生内存溢出时,会将该类对象进行回收。提供SoftReference类来实现弱引用。
弱引用:也是非必需对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。提供WeakReference类来实现弱引用。
虚引用:最弱的引用关系,无法通过一个虚引用来取得一个对象实例。提供了PhantomReference类来实现虚引用。
垃圾收集算法
1.标记-清除算法
定义:分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
缺点:效率低;会产生大量不连续的内存碎片。
2.复制算法
定义:首先将内存分为大小相等的两部分,每次只使用其中的一部分,当这一块内存用完了就将还存活着的对象复制到另外一块内存上,然后再把已使用过的那一块内存一次清理掉。
优点:不会产生内存碎片。
缺点:每次都只是用内存的一半。
解决:新生代中的对象大部分都是“朝生夕死”的,所以不要按1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将存活的对象复制到另外一块Survivor空间上,最后清理掉Eden和使用过的Survivor空间。(HotSpot虚拟机默认Eden和Survivor的大小比例是8:1)。但是有一个问题,我们没办法保证每次回收都只有不超过10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(老年代)进行分配担保。
3.标记-整理算法
定义:标记出所有需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法
当前的商业虚拟机的垃圾收集都采用”分代收集“算法,根据对象存活周期的不同将内存划分为几块。一般把Java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。在新生代中的对象都是”朝生夕死“的,选用复制算法。在老年代中,对象存活率高、没有额外的空间进行分配担保,必须使用标记-清除和标记-整理算法来回收。
参考自:《深入理解Java虚拟机》