垃圾收集算法

垃圾收集器是Java虚拟机中自带的功能,它的目的是帮助我们管理内存,正是因为有它的存在所以,我们在开发时,基本不用考虑内存溢出等问题。基本不用考虑不代表,一定不会遇到内存溢出等问题,在上一篇中我们用简单的方法模拟了一些内存溢出的问题,在这一篇我们将重点分享一下,Java中垃圾收集器的一些实现算法知识,了解这方面的知识有助于,在内存溢出时,快速的解决问题。垃圾收集器的主要功能有3个。

  • 哪些内存需要回收
  • 什么时候回收
  • 如何回收

实现上述的3个功能,有很多种算法,下面我们具体看一下每一种算法的利与弊

  • 引用计数算法

给对象添加一个引用计数器,每当有对象引用时,计数器的值就加1。当引用失效时,计数器的值就减1。这样当对象的计数器为0时,该对象就是没有被引用的对象,也就是可以被回收的对象了。但这种算法有一个弊端,就是如果对象循环引用时,这两个对象的引用计数器的值都不为0,这样,垃圾收集器就无法准时的回收它们了。正是因为有这样的弊端,所以在Java虚拟机中的垃圾收集器并不是采用这种算法实现的。

  • 可达性分析算法

通过称为“GC Roots”的对象作为起始点,从这些节点开始搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,就说明当前对象是不可用的。 也就是可以被垃圾收集器回收的对象。

在Java中,可以做为GC Roots的对象主要包括下面几种:

  • 栈中的引用对象
  • 方法区中类静态属性引用对象
  • 方法区中常量引用对象

上面我们提到了引用,下面我们看一下引用的介绍。引用:如果某类型中的数据存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。在Java中引用主要包括4种它们分别是:

  • 强引用:强引用是永远不会被垃圾收集器回收的对象。类似于new 一个新对象时创建的引用。
  • 软引用:软引用是用来描述一些有用但并非必需的对象。当系统将要发生内存溢出时,会把这些对象列为待回收对象,但不会马上回收,当垃圾收集器执行第二次时回收此范围的对象。
  • 弱引用也是用来描述非必需对象的,它的强度比软引用更弱。当垃圾收集器执行时,优先回收此类型的引用对象。无论当前内存是否足够,都会回收。
  • 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。虚引用的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知。

在可达性分析算法中当真真正正确认一个对象是否可以回收,通常要经历两次标记过程。当对象进行分析后,发现没有与GC Roots相连接的引用链,那么它还会对已经标记的对象,进行最后一次筛选,目的是判断对象是否可以执行finalize()方法。 当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机将回收此对象。如果对象覆盖了finalize()方法,并且在finalize()方法中重新创建了新引用,那么虚拟机将在第二次标记时,将此对象移除出“即将回收”的集合。如果对象在虚拟机执行finalize()方法时还没有任何引用链,那此对象将基本被回收。finalize()方法是对象防止被垃圾收集器回收的最后机会。

  • 标记-清除算法

算法主要分为标记和清除两个阶段。首先要标记出所有需要被回收的对象,然后在所有对象标记完成后,在统一回收已标记的对象。这种算法有两个弊端:一个是效率问题,标记和回收时所消耗的时间比较长。别一个弊端就是在回收时因为对象有可能不是连续的,所以导致回收后的内存也是不连续的,也就是会造成很多内存碎片,内存碎片大多有一个问题,就是如果虚拟机要分配大的对象时,由于无法找到满足条件的足够的连续内存,所以不得不在一次触发一次垃圾收集,所以性能较慢。

  • 复制算法

在标记-清除算法中因为有效率等问题,所以,复制算法出现了。它基本逻辑是将内存分为大小相等的两块,每次只使用其中的一块。 当这一块的内存用完时,就将还存活着的对象(也是采用标记的方式)复制到另外一边,然后再把这一块的内存回收。 这样使回收后的内存不会在有内存碎片了。 但这种方式也有弊端就是它将内存划分了两半,每次使用时只能用一半内存来存储数据。还有一个弊端就是,因为要复制已经存活的对象,如果存活的对象比较多时,复制时所消耗的时间比较大,效率比较低。

  • 标记-整理算法

标记-整理算法也是先采用标记方式,找出所有存活的对象,然后不在是复制方式,而是将所有存活的对象都向一端移动,然后在回收边界以外的内存。

  • 分代收集算法

分代收集算法是根据对象的存活周期的不同将内存划分为不同的几块。 一般是把堆分为新生代和老年代。 在新生代中因为大部分对象都不是存活的,所以只要进行少量的复制即可,所以采用复制算法。 而老年代中因为大部分对象都是存活的,如果拷贝的话,效率会有影响,于是就就采用“标记—清理”或者“标记—整理”算法来进行回收。垃圾收集器是Java虚拟机中自带的功能,它的目的是帮助我们管理内存,正是因为有它的存在所以,我们在开发时,基本不用考虑内存溢出等问题。基本不用考虑不代表,一定不会遇到内存溢出等问题,在上一篇中我们用简单的方法模拟了一些内存溢出的问题,在这一篇我们将重点分享一下,Java中垃圾收集器的一些实现算法知识,了解这方面的知识有助于,在内存溢出时,快速的解决问题。垃圾收集器的主要功能有3个。

  • 哪些内存需要回收
  • 什么时候回收
  • 如何回收

实现上述的3个功能,有很多种算法,下面我们具体看一下每一种算法的利与弊

  • 引用计数算法

给对象添加一个引用计数器,每当有对象引用时,计数器的值就加1。当引用失效时,计数器的值就减1。这样当对象的计数器为0时,该对象就是没有被引用的对象,也就是可以被回收的对象了。但这种算法有一个弊端,就是如果对象循环引用时,这两个对象的引用计数器的值都不为0,这样,垃圾收集器就无法准时的回收它们了。正是因为有这样的弊端,所以在Java虚拟机中的垃圾收集器并不是采用这种算法实现的。

  • 可达性分析算法

通过称为“GC Roots”的对象作为起始点,从这些节点开始搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,就说明当前对象是不可用的。 也就是可以被垃圾收集器回收的对象。

在Java中,可以做为GC Roots的对象主要包括下面几种:

  • 栈中的引用对象
  • 方法区中类静态属性引用对象
  • 方法区中常量引用对象

上面我们提到了引用,下面我们看一下引用的介绍。引用:如果某类型中的数据存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。在Java中引用主要包括4种它们分别是:

  • 强引用:强引用是永远不会被垃圾收集器回收的对象。类似于new 一个新对象时创建的引用。
  • 软引用:软引用是用来描述一些有用但并非必需的对象。当系统将要发生内存溢出时,会把这些对象列为待回收对象,但不会马上回收,当垃圾收集器执行第二次时回收此范围的对象。
  • 弱引用也是用来描述非必需对象的,它的强度比软引用更弱。当垃圾收集器执行时,优先回收此类型的引用对象。无论当前内存是否足够,都会回收。
  • 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。虚引用的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知。

在可达性分析算法中当真真正正确认一个对象是否可以回收,通常要经历两次标记过程。当对象进行分析后,发现没有与GC Roots相连接的引用链,那么它还会对已经标记的对象,进行最后一次筛选,目的是判断对象是否可以执行finalize()方法。 当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机将回收此对象。如果对象覆盖了finalize()方法,并且在finalize()方法中重新创建了新引用,那么虚拟机将在第二次标记时,将此对象移除出“即将回收”的集合。如果对象在虚拟机执行finalize()方法时还没有任何引用链,那此对象将基本被回收。finalize()方法是对象防止被垃圾收集器回收的最后机会。

  • 标记-清除算法

算法主要分为标记和清除两个阶段。首先要标记出所有需要被回收的对象,然后在所有对象标记完成后,在统一回收已标记的对象。这种算法有两个弊端:一个是效率问题,标记和回收时所消耗的时间比较长。别一个弊端就是在回收时因为对象有可能不是连续的,所以导致回收后的内存也是不连续的,也就是会造成很多内存碎片,内存碎片大多有一个问题,就是如果虚拟机要分配大的对象时,由于无法找到满足条件的足够的连续内存,所以不得不在一次触发一次垃圾收集,所以性能较慢。

  • 复制算法

在标记-清除算法中因为有效率等问题,所以,复制算法出现了。它基本逻辑是将内存分为大小相等的两块,每次只使用其中的一块。 当这一块的内存用完时,就将还存活着的对象(也是采用标记的方式)复制到另外一边,然后再把这一块的内存回收。 这样使回收后的内存不会在有内存碎片了。 但这种方式也有弊端就是它将内存划分了两半,每次使用时只能用一半内存来存储数据。还有一个弊端就是,因为要复制已经存活的对象,如果存活的对象比较多时,复制时所消耗的时间比较大,效率比较低。

  • 标记-整理算法

标记-整理算法也是先采用标记方式,找出所有存活的对象,然后不在是复制方式,而是将所有存活的对象都向一端移动,然后在回收边界以外的内存。

  • 分代收集算法

分代收集算法是根据对象的存活周期的不同将内存划分为不同的几块。 一般是把堆分为新生代和老年代。 在新生代中因为大部分对象都不是存活的,所以只要进行少量的复制即可,所以采用复制算法。 而老年代中因为大部分对象都是存活的,如果拷贝的话,效率会有影响,于是就就采用“标记—清理”或者“标记—整理”算法来进行回收。

发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();