最近学习了一下java垃圾回收机制,将其主要内容大致总结一下:

1.什么是垃圾回收机制

  java GC机制(garbage collection,垃圾收集,垃圾回收),是java特有的机制,作为java开发者,一般不需要专门编写内存回收和垃圾清理代码。这是因为在java虚拟机中,存在自动内存管理和垃圾清理机制,为java开发者节省了大量的开发时间。

  概括地说,该机制对 JVM(Java Virtual Machine)中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop)的保证JVM中的内存空间,放置出现内存泄露和溢出问题。

  下面就来说一说java垃圾回收机制的回收策略,不过首先得了解jvm内存模型。

2.jvm内存模型

  ①程序计数器:程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。每个程序计数器只用来记录一个线程的行号,因此它是线程私有的。通俗点说就是,当cpu执行到一个线程的某个位置时,切换去执行另一个线程,当cpu下次再切回来时会继续从上次切出去的位置继续往下执行。怎么确定上次切出去的位置,就是通过程序计数器实现的。

  ②虚拟机栈:一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。

  局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。在局部变量表中,只有long和double类型会占 用2个局部变量空间(Slot,对于32位机器,一个Slot就是32个bit),其它都是1个Slot。需要注意的是,局部变量表是在编译时就已经确定 好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。

  虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError(栈溢出);不过多 数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,知道内存不足,此时,会抛出 OutOfMemoryError(内存溢出)。

  每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

  ③本地方法栈:本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。

  本地方法栈也是线程私有的。

  ④堆区:堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)

  ⑤方法区:方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等

  3.垃圾回收算法

  如何高效地进行垃圾回收。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。

  ①.Mark-Sweep(标记-清除)算法

  这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:

  从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

  ②.Copying(复制)算法

  为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:

  这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。

  很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

  ③.Mark-Compact(标记-整理)算法

  为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:

  

  ④.Generational Collection(分代收集)算法

  分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

  

  新生代(Young generation): 绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可到达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为”minor GC“。

  老年代(Old generation): 对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代少得多。对象从老年代中消失的过程,我们称之为”major GC“(或者”full GC“)

  目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

  而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

  注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。

  4.典型的垃圾收集器

  

JDK7一共有5种GC类型:

  1. Serial GC

  2. Parallel GC

  3. Parallel Old GC (Parallel Compacting GC)

  4. Concurrent Mark & Sweep GC  (or “CMS”)

  5. Garbage First (G1) GC

其中,Serial GC不应该被用在服务器上。这种GC类型在单核CPU的桌面电脑时代就存在了。使用Serial GC会显著的降低应用的性能指标。

现在,让我们共同学习每一种GC类型

①. Serial GC (-XX:+UseSerialGC)

新生代空间的GC方式我们在前面已经介绍过了,在老年代空间中的GC采取称之为”mark-sweep-compact“的算法。

  1. 算法的第一步是标记老年代中依然存活对象。(标记)

  2. 第二步,从头开始检查堆内存空间,并且只留下依然幸存的对象。(清理)

最后一步,从头开始,顺序地填满堆内存空间,并且将对内存空间分成两部分:一个保存着对象,另一个空着(压缩)。

②. Parallel GC (-XX:+UseParallelGC)

图 1: Serial GC 与 Parallel GC的区别

从上图中,你可以轻易地看出serial GC和parallel GC的区别,serial GC只使用一个线程执行GC,而parallel GC使用多个线程,因此parallel GC更高效。这种GC在内存充足以及多核的情况下会很有用,因此我们也称之为”throughput GC“。

③. Parallel Old GC(-XX:+UseParallelOldGC)

Parallel Old GC在JDK5之后出现。与parallel GC相比,唯一的区别在于针对老年代的GC算法。Parallel Old GC分为三步:标记-汇总-压缩(mark – summary – compaction)。汇总(summary)步骤与清理(sweep)的不同之处在于,其将依然幸存的对象分发到GC预先处理好的不同区域,算法相对清理来说略微复杂一点。

④. CMS GC (-XX:+UseConcMarkSweepGC)

图 2: Serial GC & CMS GC

就像你从上图看到的那样, CMS GC比我之前解释的各种算法都要复杂很多。第一步初始化标记(initial mark) 比较简单。这一步骤只是查找那些距离类加载器最近的幸存对象。因此,停顿的时间非常短暂。在之后的并行标记( concurrent mark )步骤,所有被幸存对象引用的对象会被确认是否已经被追踪和校验。这一步的不同之处在于,在标记的过程中,其他的线程依然在执行。在重新标记(remark)步骤,会再次检查那些在并行标记步骤中增加或者删除的与幸存对象引用的对象。最后,在并行交换( concurrent sweep )步骤,转交垃圾回收过程处理。垃圾回收工作会在其他线程的执行过程中展开。一旦采取了这种GC类型,由GC导致的暂停时间会极其短暂。CMS GC也被称为低延迟GC。它经常被用在那些对于响应时间要求十分苛刻的应用之上。

当然,这种GC类型在拥有stop-the-world时间很短的优点的同时,也有如下缺点:

  • 它会比其他GC类型占用更多的内存和CPU

  • 默认情况下不支持压缩步骤

在使用这个GC类型之前你需要慎重考虑。如果因为内存碎片过多而导致压缩任务不得不执行,那么stop-the-world的时间要比其他任何GC类型都长,你需要考虑压缩任务的发生频率以及执行时间。

⑤. G1 GC

最后,我们来学习垃圾回收优先(G1)GC类型。

图 3:  G1 GC的结构

  如果你想要理解G1,首先你要忘记你所学过的新生代和老年代的概念。正如你在上图所看到的,每个对象被分配到不同的格子,随后GC执行。当一个区域装满之后,对象被分配到另一个区域,并执行GC。这中间不再有从新生代移动到老年代的三个步骤。这个类型是为了替代CMS GC而被创建的,因为CMS GC在长时间持续运作时会产生很多问题。

参考文章:http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html

         http://www.cnblogs.com/dolphin0520/p/3783345.html

   

java垃圾回收机制学习总结的更多相关文章

  1. Java 垃圾回收机制学习

    原文链接: http://blog.csdn.net/zsuguangh/article/details/6429592 自己学习总结: 1c++和java的内存使用的区别: 在C++中,对象所占的内 ...

  2. Java垃圾回收机制(转)

    原文链接:Java垃圾回收机制 1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内 ...

  3. [转载]深入理解Java垃圾回收机制

    深入理解Java垃圾回收机制 2016-07-28 20:07:49 湖冰2019 阅读数 14607更多 分类专栏: JAVA基础   原文:http://www.linuxidc.com/Linu ...

  4. 【转载】Java垃圾回收机制

    原文地址:http://www.importnew.com/19085.html Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联 ...

  5. 【转】深入理解 Java 垃圾回收机制

    深入理解 Java 垃圾回收机制   一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  6. 深入理解java垃圾回收机制

    深入理解java垃圾回收机制---- 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  7. Java垃圾回收机制_(转载)

    Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给 ...

  8. 成为Java GC专家(3)—如何优化Java垃圾回收机制

    为什么需要优化GC 或者说的更确切一些,对于基于Java的服务,是否有必要优化GC?应该说,对于所有的基于Java的服务,并不总是需要进行GC优化,但前提是所运行的基于Java的系统,包含了如下参数或 ...

  9. java 垃圾回收机制 引用类型

    Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对 ...

随机推荐

  1. Scala语言笔记 - 第二篇

    目录 1 Map的基础操作 2 Map生成view和transform解析 ​ 最近研究了下scala语言,这个语言最强大的就是它强大的函数式编程(Function Programming)能力,记录 ...

  2. 实现SLIC算法生成像素画

    前言 像素风最早出现在8bit的电子游戏中,受制于电脑内存大小以及显示色彩单一, 只能使用少量像素来呈现内容,却成就了不少经典的像素游戏.随着内存容量与屏幕分辨率的提升,内存与显示媒介的限制不再是问题 ...

  3. Spring WebFlux 教程:如何构建反应式 Web 应用程序

    Spring WebFlux 教程:如何构建反应式 Web 应用程序 反应式系统提供了我们在高数据流世界中所需的无与伦比的响应能力和可扩展性.然而,反应式系统需要经过专门培训的工具和开发人员来实现这些 ...

  4. ES6学习笔记之 let与const

    在js中,定义变量时要使用var操作符,但是var有许多的缺点,如:一个变量可以重复声明.没有块级作用域.不能限制修改等. //缺点1:变量可以重复声明 var a=1; var a=2; conso ...

  5. VRRP简介以及配置案例

    一.背景 二.VRRP 概念介绍 三.实验操作 一.背景 局域网中的用户终端通常采用配置一个默认网关的形式访问外部网络,如果此时默认网关设备发生故障,将中断所有用户终端的网络访问,这很可能会给用户带来 ...

  6. 关于Ubuntu的超级管理员Root的切换及初始密码设置

    背景介绍 总有一些操作,可能需要更高的超级管理员权限才能进行,甚至才可见有些文件,所以在Linux中我们需要切换到Root用户,也就是对应的Windows的Administrator账户. 从当前用户 ...

  7. 10 一键部署LNMP网站平台

    #!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin NGINX_V=1.19.1 P ...

  8. 45、django工程(URLconf)

    45.1.django URLconf 路由系统介绍: 1.说明: URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表, ...

  9. 不止Docker:8款容器管理开源方案

    Docker诞生于2013年,并普及了容器的概念,以至于大多数人仍然将容器的概念等同于"Docker容器". 作为第一个吃螃蟹的人,Docker设置了新加入者必须遵守的标准.例如, ...

  10. Java:Apache Commons 工具类介绍及简单使用

    Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动.下面是我这几年做开发过程中自己用过的工具类做简单介绍. Commons简介 组件 功能介绍 commo ...