jvm垃圾回收机制和内存回收算法

垃圾回收算法

1.  引用计数器算法

引用计数器算法是给每一个对象设置一个计数器,当有地方引用这一个对象时,计数器加一,当引用失效时,计时器减一。当计数器为0时,jvm就认为这个对象不再被使用,标记为“垃圾”。这种算法虽然实现简单,效率高。但是很难解决对象之间循环引用的问题。当A引用B,B引用A,但是两个对象已不再被其他任何对象引用。(举个例子:两个对象objA和objB中都有字段instance,赋值,objA.instance=objB,objA.instance=objA,除此之外两个对象不再被其他对象引用,但是他们之间相互引用,导致计数器都不为0,于是无法通知GC收集器回收他们)同时,这种算法带来了很多的额外开销。

  1. 可达性分析算法

    1. 在主流的编程语言中的主流的实现中,都是通过可达性分析算法来判断对象是否存活,这个算法的基本思路就是通过一系列的”GC Roots”对象作为起始点,从这些节点开始像下搜索,搜索走过的路径称为引用链(Reference Chain)当一个对象到GC Roots 没有任何的引用链相连时,则证明该对象不可用,
    2. 在java 语言中可作为GC Roots的对象包括
      1. 虚拟机栈中引用的对象。
      2. 方法区中类静态属性引用的对象
      3. 方法区中常量引用的对象。
      4. 本地方法栈zhongJNI引用的对象

何为强引用,弱引用,软引用,和虚引用?

无论是通过引数计数器算法还是可达性分析算法,判断一个对象是否存活都与“引用” 有关,在jdk1.2 之前,Java中引用的定义很狭隘:如果reference类型的数据指向另一块内存的起始地址,就成为这块内存代表着一个引用,在这中定义之下就只有引用和没有被引用两种情况。但是我们希望能有一类对象,在内存 充足时,保留在系统中,系统内存不足时,回收。在JDK1.2之后,将应用分为强引用,软引用,弱引用,虚引用。

  • 强引用在程序代码中普遍存在,例如 Object A = new Object();
    这类引用,只要强引用还存在,垃圾收集器就不会回收被引用的对象。

  • 软引用用来描述一些有用但是非必要的对象,用软引用关联着的对象在系统将要发生内存溢出之前将会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存将会抛出内存溢出我的异常

  • 弱引用也是用来描述,非必须对象的,但是它的强度比软引用弱一些被弱引用关联的对象只能生存到下一次,垃圾回收发生之前当垃圾收集器,工作时,无论内存是否足够都会回收掉只被弱引用关联的对象
  • 虚引用,也称为幽灵引用,或者幻影引用,他是最弱的一种引用关系.一个对象是否有需引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象的实例,为一个对象设置引用关联的唯一目的就是能在这个对象,被回收之前收到一个系统通知,

    垃圾收集算法

    • 第一个,标记清除算法,最基础的就是标记清除算法,标记-清除法分为标记和清除2个阶段。首先标记出所有需要回收的对象,标记完成后统一回收被标记的对象,标记的过程其实其实就是之前介绍的两种垃圾回收算法。之所以说它是最基础的收集方法,是因为后续的收集算法都是基于这种思路,并对其不足而进行改造。主要有两个问题,第一个就是效率问题,标记和清除2个过程效率都不高,另一个就是空间问题,标记清楚之后会产生大量不连续的内存碎片,内存碎片太多,可能会导致以后,程序在运行过程中需要分配较大内存是无法找到足够连续内存而不得不提前触发另一次垃圾收集动作
    • 第二个就是复制算法,为了解决效率问题一种称为复制算法的设计方法,就出现现了大将可用内存按照容量划分为大小相等的两块每次只使用其中一块当这块内存用完了,就江海存活的对象复制到另一块上面然后再把一使用过的,内存空间一次清理掉这样每次都是对着整个半区进行内存回收,内存分配时不需要考虑内存碎片的情况。只要一动对顶的指针按顺序分配内存即可实现简单运行高效。这种算法的代价是将内存缩小为原来的一半java虚拟机,现在虚拟机都是使用这种算法来回收新生代。IBM研究表明,在新生代中的98%的对象都是“朝生夕死”,所以不需要按照1:1的比例来进行分配两块内存。而是将内存分为一块较大的Eden空间和两块较小的Survivor,两个的大小比例为8:1。每次使用一块Eden和一块Survivor,在回收时,将Eden和Survivor中的存活对象复制到另一块Survivor中。新生代中每次可用的内存为总内存的90%,但是我们没有办法保证存活的对象不超过10%,这个时候就需要依赖其他内存进进行分配担保
    • 标记整理算法,复制收集算法,在对象存活率较高时,就需要进行较多的复制操作效率会降将会变低,更关键的是如果你不想浪费50%的空间,就需要额外的空间进行分配,担保以应对被使用内存中所有对象百分之百存活的极端情况,所以在老年代一般不能使用,这种算法根据老年代的特点,有人提出了另一种标记-整理算法,标记整理算法过程与标记清除算法一样,但后续步骤不是对可回收的对象进行清理,而是直接将所有存活的对象都向一段移动,然后直接清理掉边界外的内存
    • 分代收集算法,当前商业虚拟机都采用分代收集算法,根据对象生存周期的不同,将内存分为几块。一般是把java堆分为新生代和老年代,新生代和老年代这样就可以根据各个年代的特点,采用最适当的收集算法。在新生代中,每次垃圾收集时都会发现有大批的对象死去,只有少量的对象存活那就采用复制算法,只需要复制出少量存活的对象就可以完成收集。二,老年代中对象存活率高,没有额外的空间,对进行分别担保,就必须猜用标记清理或者标记整理算法来进行回收

猜你喜欢

转载自blog.csdn.net/qq_28938627/article/details/80574830