JVM

引用计数法:在Java领域,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存,主要原因是,这个看似简单的算法有很多例外情况要考虑,必须要配合大量额外处理才能保证正确地工作,譬如单纯的引用计数就很难解决对象之间相互循环引用的问题。
可达性分析
  ○ 通过一系列称为“GCRoots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
  ○ Object5、6、7都将被回收
  

  ○ 固定可作为GC Roots的对象
    § 虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
    § 静态属性引用的对象、常量引用的对象
    § 被同步锁(synchronized)持有的对象
    § JVM内部的引用(基本数据类型对应的class对象、常驻异常对象、系统类加载器)
    § JNI引用的对象
    § ...
引用的分类(引用强度依次减弱)
  ○ 强引用:普通引用,存在最多,例如new
  ○ 软引用(SoftReference):在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
  ○ 弱引用(WeakReference):当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
  ○ 虚引用(PhantomReference):一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
判定是否回收
  ○ 在进行可达性分析后,如果一个对象没有与GC Roots相连接的引用链,它并不会被立即回收,还要经过一次筛选,假如对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,那么虚拟机将这两种情况都视为“没有必要执行”。
方法区的垃圾回收条件苛刻,不常进行

分代收集
  ○ 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
  ○ 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。
收集的分类
  ○ 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集
    § 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
    § 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。另外请注意“MajorGC”这个说法现在有点混淆,在不同资料上常有不同所指,读者需按上下文区分到底是指老年代的收集还是整堆收集。
    § 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
  ○ 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
区域划分
  ○ 新生代、老年代:在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。
  ○ 跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说仅占极少数。所以,在对新生代单独做垃圾收集时,不应该为了少量的跨代引用而遍历整个老年代(搜索引用链)。
  ○ 全局的数据结构——记忆集(Remembered Set):把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。
标记-清除算法
  ○ 算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。
  ○ 缺点
    § 执行效率不稳定:若存在大量要回收的对象,则大量的标记和清除操作会降低效率。
    § 内存空间碎片化:在执行若干次标记清除之后,磁盘空间将变得碎片化,当难以分配空间给较大对象时,则必须再执行标记清除。
      

标记-复制算法
  ○ 半区复制:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
  ○ 当要回收的半区中大部分对象是可以存活的,这将带来很大的复制开销;但当大部分对象是需要回收时,只需执行少量的复制操作和一次清除操作即可。所以,这种算法很适合新生代区域的回收。
  ○ 缺点是空间被浪费了一半(必须闲置一半的空间用于复制)。但实际应用中,设计者并没有按照1:1来划分区域,因为98%的新生代对象死在第一轮收集。
标记-整理算法
  ○ 让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。
  ○ 弊端:对于老年代需要大量移动对象;移动对象时需要全程暂停应用程序。
• 选择
  ○ 标记-清除算法在回收时更为直接,但由于空间碎片化,需要依赖于更复杂的内存分配和访问机制,而内存访问是非常频繁的,会直接影响程序的吞吐量。
  ○ 是否移动对象都存在弊端,移动则内存回收时会更复杂,不移动则内存分配时会更复杂。
  ○ HotSpot虚拟机里面关注吞吐量的Parallel Scavenge收集器是基于标记-整理算法的,而关注延迟的CMS收集器则是基于标记-清除算法的。
平时多数时间都采用标记-清除算法,暂时容忍内存碎片的存在,直到内存空间的碎片化程度已经大到影响对象分配时,再采用标记-整理算法收集一次,以获得规整的内存空间。

参考资料:《深入理解Java虚拟机 第三版》周志明

原文地址:https://www.cnblogs.com/CofJus/p/13426267.html