yunshuipiao/Potato

GC And Algorithm

Opened this issue · 0 comments

GC And Algorithm

Table of Contents GC And AlgorithmJVM 内存管理GC 机制查找算法四个引用内存分区GC 算法垃圾收集器串行收集器(Serial GC)ParNew GCParallel Scavenge GCCMS (Concurrent Mark Sweep)收集器….总结

这篇文章讲述 java 的垃圾回收机制,和之前的文章有密切的联系。
JMM, JVM, Java Class Model

在平常的开发中,如果不了解内存回收机制的原理,可能会引发内存泄漏,频繁 GC。导致应用卡顿,甚至崩溃等。

JVM 内存管理

这里在之前的基础上,详细介绍每一个区域的作用。

  1. 方法区:存放已被加载的类的信息,如(类名,修饰符),静态变量,构造函数,final定义的常量,类中的字段和方法等信息。全局共享。但方法区超过允许的大小时,会发生 OOM。
    运行时常量是方法区的一部分,用于存储编译器生成的常量引用。
  2. 堆区:GC 最频繁的区域,也是理解 GC 机制最重要的区域。有所有线程共享,在虚拟机启动时创建。堆区主要用于存放对象实例及数组,所有 new 出来的对象都存储在该区域。
  3. 虚拟机栈:占用操作系统内存,每个线程对应一个虚拟机栈,线程私有,生命周期和线程一样,每个方法被执行时产生一个栈帧,用于存储局部变量表,动态链接,操作数和方法出口等信息。当方法被调用时,栈帧入栈,当方法调用结束,栈帧出战。
    虚拟机栈定义了两种异常类型,StackOverFlowError 和 OOM。如果线程调用的深度大于虚拟机允许的最大深度,则抛出 StackOverFlowError;直到内存不足时,抛出 OOM。
  4. 本地方法栈:用于支持 native 的执行。
  5. 程序计数器:JVM在解释字节码(.class)文件时,存储当前线程执行的字节码行号,只是一种概念模型,各种JVM所采用的方式不一样。
    此内存区是唯一不会抛出OutOfMemoryError的区域。

GC 机制

随着程序的运行,内存中的实例对像,变量占用的内存越来越多,如果不及时进行回收,就会降低程序运行效率,甚至引发系统异常。

在上面的区域中,有 3 个区域不需要进行垃圾回收,本地方法栈,虚拟方法栈和程序计数器。因为生命周期和线程同步,线程销毁和其占用内存也会销毁。因此,只有方法区和堆区需要进行垃圾回收,回收的对象就是那些不存在引用的对象。

查找算法

  1. 引用计数法:基本概念不做介绍。当存在相互引用时,无法回收。
  2. 根搜索算法(可达性分析):从一个称作 GC Roots 的根基点出发,向下搜索。如果一个对象不能达到 GC Roots 的时候,说明该对象不再被引用,可以回收。
    可作为 GC Roots 的对象:
    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中的 JNI (Native)引用的方法

四个引用

在JDK1.2之后引入了四个概念:强引用、软引用、弱引用、虚引用

  • 强引用:new 出来的对象都是强引用,GC 对象无论如何都不会回收,即使 OOM。
  • 软引用:只有当 JVM 内存不足时才会回收,用于实现高速缓存
  • 弱引用:只要发生 GC,就会立马回收,不存内存是否充足(常用)
  • 虚引用:起到跟踪记录的作用,忽略不计。

内存分区

内存主要被分为三代,新声代,旧生代和持久代。不同的特点造成了其回收算法的不同,新生代适合生命周期短,快速创建和销毁的对象;旧生代适合生命周期较长的对象,持久代在 Sun Hotpot 虚拟机中就是指方法区。

  • 新生代:大概分为 Eden 区和 Survivor 区。而 Survivror 区又分为大小相同的部分:FromSpace 和 ToSpace。新建的对象都是从新生代分配内容, Eden 不足时,会把存活的对象转移到 Survivor 区。当新生代进行垃圾回收时会发生 Minor GC。
  • 旧生代:用于存放新生代多次回收依然存活的对象, 如缓存对象。当旧生代满时,需要进行回收,称为 Major GC(Full GC)。
  • 持久代:在 Sun 的 JVM 中是方法区。

GC 算法

  • 标记-清除:该算法采用的是从根集合开始扫描,将存活的对象进行标记,标记完毕后,再扫描整个空间中为被标记的对象,并进行清除。由于只是清除,会造成内存碎片。
  • 复制:复制算法采用的方式为从根集合进行扫描,将存活的对象移动到一块空闲的区域。当存活的对象较少时,复制算法会比较高效(新生代 Eden 采用),其成本是需要一块额外的空间对象和内存的移动。
  • 标记-压缩:与标记-清除算法类似,都是先对存活的对象进行标记,但是在清除之后会把或的对象向左端进行空间移动,然后再更新其引用对象的指针。使用与旧生代。
  • 分代收集:当前商业虚拟机都采用此算法。根据对象存活周期的不同讲内存划分为几块。一般把 Java 堆分为新生代和老年代。前者每次垃圾回收都有大量对象死去,少量存活,使用复制算法;老年代存活率高,没有额外空间对其进行分配担保,必须使用 标记—清理 或者 标记—整理 算法进行回收。

垃圾收集器

GC 是由垃圾回收器来执行。在实际场景中,需要选择合适的垃圾收集器。

串行收集器(Serial GC)

Serial GC是最古老也是最基本的收集器,但是现在依然广泛使用,JAVA SE5和JAVA SE6中客户端虚拟机采用的默认配置。比较适合于只有一个处理器的系统。

在串行处理器中,minor 和 major GC过程中都是用一个线程进行回收。最大特点是在进行垃圾收回是,需要对所有正在进行的线程暂停,对于有些应用难以接受。

ParNew GC

基本过程和串行收集器一样,但加入了多线程机制,提高了效率。

Parallel Scavenge GC

在整个扫描和复制过程采用多线程的方式进行,适用于多CPU、对暂停时间要求较短的应用,是server级别的默认GC方式。

CMS (Concurrent Mark Sweep)收集器

该收集器的目标是解决Serial GC停顿的问题,以达到最短回收时间。常见的B/S架构的应用就适合这种收集器,因为其高并发、高响应的特点,CMS是基于标记-清楚算法实现的。

CMS收集器的优点:并发收集、低停顿,但远没有达到完美;

….

总结

深入理解JVM的内存模型和GC机制有助于帮助我们编写高性能代码和提供代码优化的思路与方向。