怎么自己做网站app,网站开发设计的技术,松岗做网站价格,行业网址大全垃圾回收机制是由垃圾收集器Garbage Collection GC来实现的#xff0c;GC是后台的守护进程。它的特别之处是它是一个低优先级进程#xff0c;但是可以根据内存的使用情况动态的调整他的优先级。因此#xff0c;它是在内存中低到一定限度时才会自动运行#xff0c;从而实现对… 垃圾回收机制是由垃圾收集器Garbage Collection GC来实现的GC是后台的守护进程。它的特别之处是它是一个低优先级进程但是可以根据内存的使用情况动态的调整他的优先级。因此它是在内存中低到一定限度时才会自动运行从而实现对内存的回收。这也是垃圾回收的时间不确定的原因 为何要这样设计因为GC也是进程也要消耗CPU等资源如果GC执行过于频繁会对java的程序的执行产生较大的影响java解释器本来就不快因此JVM的设计者们选着了不定期的gc。 一、为什么要进行垃圾回收
我们知道Java是一门面向对象的语言在一个系统运行中会伴随着很多对象的创建而这些对象一旦创建了就占据了一定的内存在上一篇中我们介绍过创建的对象是保存在堆中的当对象使用完毕之后不对其进行清理那么会一直占据内存空间很明显内存空间是有限的如果不回收这些无用的对象占据的内存那么新创建的对象申请不了内存空间系统就会抛出异常而无法运行所以必须要经常进行内存的回收也就是垃圾收集。
二、回收
在堆里面存放着Java世界中几乎所有的对象实例垃圾收集器在对堆进行回收前第一 件事情就是要确定这些对象之中哪些还“存活”着哪些已经“死去”即不可能再被任何途径 使用的对象。
1、引用计数算法
引用计数是垃圾收集器中的早期策略。在这种方法中堆中每个对象实例都有一个引用计数。当一个对象被创建时就将该对象实例分配给一个变量该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时计数加1a b,则b引用的对象实例的计数器1但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时它引用的任何对象实例的引用计数器减1
优缺点
优点引用计数收集器可以很快的执行交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。
缺点无法检测出循环引用。如父对象有一个对子对象的引用子对象反过来引用父对象。这样他们的引用计数永远不可能为0。
2、可达性分析算法
可达性分析算法是从离散数学中的图论引入的程序把所有的引用关系看作一张图从一个节点GC ROOT开始寻找对应的引用节点找到这个节点以后继续寻找这个节点的引用节点当所有的引用节点寻找完毕之后剩余的节点则被认为是没有被引用到的节点即无用的节点无用的节点将会被判定为是可回收的对象。 在Java语言中可作为GC Roots的对象包括下面几种 a) 虚拟机栈中引用的对象栈帧中的本地变量表 b) 方法区中类静态属性引用的对象 c) 方法区中常量引用的对象 d) 本地方法栈中JNINative方法引用的对象。 这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点从这些节点开始向下搜索搜索所走过的路径称为引用链Reference Chain当一个对象到GC Roots没有任何引用链相连用图论的话来说就是从GC Roots到这个对象不可达时则证明此对象是不可用的。如图所示对象object 5、object 6、object 7虽然互相有关联但是它们到GC Roots是不可达的所以它们将会被判定为是可回收的对象。
二、如何判断对象为垃圾对象
在JVM中主要的垃圾收集算法有标记-清除、标记-清除-压缩简称**“标记-整理”、标记-复制-清除简称“复制”、分代收集算法**。这几种收集算法互相配合针对不同的内存区域采取对应的收集算法实现这里具体是由相应的垃圾收集器实现
垃圾回收涉及到大量的程序细节而且各个平台的虚拟机操作内存的方式也不一样但是他们进行垃圾回收的算法是通用的所以这里我们也只介绍几种通用算法。
①、标记-清除算法
算法实现分为标记-清除两个阶段首先根据上面的根搜索算法标记出所有需要回收的对象在标记完成后然后在统一回收掉所有被标记的对象。
缺点
1、效率低标记和清除这两个过程的效率都不高。
2、容易产生内存碎片因为内存的申请通常不是连续的那么清除一些对象后那么就会产生大量不连续的内存碎片而碎片太多时当有个大对象需要分配内存时便会造成没有足够的连续内存分配而提前触发垃圾回收甚至直接抛出OutOfMemoryExecption。 ②、复制算法
为了解决标记-清除算法的两个缺点复制算法诞生了。
算法实现将可用内存按容量划分为大小相等的两块区域每次只使用其中一块当这一块的内存用完了就将还活着的对象复制到另一块区域上然后再把已使用过的内存空间一次性清理掉。
优点每次都是只对其中一块内存进行回收不用考虑内存碎片的问题而且分配内存时只需要移动堆顶指针按顺序进行分配即可简单高效。
缺点将内存分为两块但是每次只能使用一块也就是说机器的一半内存是闲置的这资源浪费有点严重。并且如果对象存活率较高每次都需要复制大量的对象效率也会变得很低。 ③、标记-整理算法
上面我们说过复制算法会浪费一半的内存并且对象存活率较高时会有过多的复制操作效率低下。
如果对象存活率很高基本上不会进行垃圾回收时标记-整理算法诞生了。
算法实现首先标记出所有存活的对象然后让所有存活对象向一端进行移动最后直接清理到端边界以外的内存。
局限性只有对象存活率很高的情况下使用该算法才会效率较高。 ④、分代收集算法
当前商业虚拟机都是采用此算法但是其实这不是什么新的算法而是上面几种算法的合集。
算法实现根据对象的存活周期不同将内存分为几块然后不同的区域采用不同的回收算法。
对于 HotSpot 虚拟机它将堆空间分为老年代和新生代两块区域
1、对于存活周期较短每次都有大批对象死亡只有少量存活的区域采用复制算法因为只需要付出少量存活对象的复制成本即可完成收集
2、对于存活周期较长没有额外空间进行分配担保的区域采用标记-整理算法或者标记-清除算法。
堆有新生代和老年代两块区域组成而新生代区域又分为三个部分分别是 Eden,From Surivor,To Survivor ,比例是8:1:1。
新生代采用复制算法每次使用一块Eden区和一块Survivor区当进行垃圾回收时将Eden和一块Survivor区域的所有存活对象复制到另一块Survivor区域然后清理到刚存放对象的区域依次循环。
老年代采用标记-清除或者标记-整理算法根据使用的垃圾回收器来进行判断。
至于为什么要这样这是由于内存分配的机制导致的新生代存的基本上都是朝生夕死的对象而老年代存放的都是存活率很高的对象。关于内存分配下篇博客我们会详细进行介绍。
四、何时进行垃圾回收
理清了什么是垃圾怎么回收垃圾最后一点就是Java虚拟机何时进行垃圾回收呢
程序员可以调用 System.gc()方法手动回收但是调用此方法表示希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行而且具体什么时候进行是取决于具体的虚拟机的不同的虚拟机有不同的对策。
其次虚拟机会自行根据当前内存大小判断何时进行垃圾回收比如前面所说的新生代满了新产生的对象无法分配内存时便会触发垃圾回收机制。
这里需要说明的是宣告一个对象死亡至少要经历两次标记前面我们说过如果对象与GC Roots 不可达那么此对象会被第一次标记并进行一次筛选筛选的条件是此对象是否有必要执行 finalize() 方法当对象没有覆盖 finalize()方法或者该方法已经执行了一次那么虚拟机都将视为没有必要执行finalize()方法。
如果这个对象有必要执行 finalize() 方法那么该对象将会被放置在一个有虚拟机自动建立、低优先级名为 F-Queue 队列中GC会对F-Queue进行第二次标记如果对象在finalize() 方法中成功拯救了自己比如重新与GC Roots建立连接那么第二次标记时就会将该对象移除即将回收的集合否则就会被回收。