java垃圾收集器与内存分配策略

概述

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

程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。而java堆和方法区则不一样,只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存。

**内存泄漏:**分配的内存得不到及时回收。
Java程序的内存分配和回收都是由JRE在后台自动进行的。(垃圾回收GC)JRE会提供一个后台线程来进行检测和控制。

【对象是否存活算法】

引用计数算法

主流的java虚拟机没有选用引用计数算法来管理内存,主要的原因是它很难解决对象之间相互循环引用的问题。

可达性分析算法

基本思路是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
java语言中,可作为GC Roots的对象包括下面几种:

  • 虚拟机栈中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI引用的对象。

引用

  • 强引用:只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
  • 软引用:SoftReference,系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
  • 弱引用:WeakReference,当垃圾收集器工作时,不管当前内存是否足够,总会回收该对象所占用的内存。
  • 虚引用:PhantomReference,完全类似于没有引用。主要用于跟踪对象被垃圾回收的状态,不能单独使用,必须和引用队列联合使用。

【垃圾收集算法】

标记-清除算法

该算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
缺点:

  1. 效率低
  2. 空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法

将可用内存按容量划分为大小相等的两块每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉
缺点:复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。

标记-整理算法

标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法

一般将java堆分为新生代老年代,这样就可以根据各个年代的特点采用最适当的收集算法。新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。老生代中,对象存活率高、没有额外空间对它进行分配担保,就必须使用**“标记-整理”算法**来进行回收。

【垃圾收集器】

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

【内存分配】

大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

参考

  • 深入理解Java虚拟机 第2版
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章