1、前言

理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序。

在学习GC前,你应该知道一个技术名词:“stop-the-world” ,无论你选择哪种GC算法,“stop-the-world”都会发生。“stop-the-world”意味着JVM停止应用程序,而去进行垃圾回收。当“stop-the-world”发生时,除了进行垃圾回收的线程,其他所有线程都将停止运行。被中断的任务将在GC任务完成后恢复执行。GC调优往往意味着减少“stop-the-world”的时间。

2、分代垃圾收集机制

在HotSpot虚拟机中,将内存分为 年轻代(young generation)、老年代(old generation) 和 永久代(permanent generation)

我们看一下这幅图:

年轻代: 新创建的对象都存放在这里。因为大多数对象很快变得不可达,所以大多数对象在年轻代中创建,然后消失。当对象从这块内存区域消失时,我们说发生了一次“minor GC”。

老年代: 没有变得不可达,存活下来的年轻代对象被复制到这里。这块内存区域一般大于年轻代。因为它更大的规模,GC发生的次数比在年轻代的少。对象从老年代消失时,我们说“major GC”(或“full GC”)发生了。

永久代: 永久代(permanent generation) 也称为“方法区(method area)”,它存储class对象和字符串常量。所以这块内存区域绝对不是永久的存放从老年代存活下来的对象的。发生在这里的垃圾回收也被称为major GC。

3、垃圾收集算法

垃圾收集算法如下:

  • 年轻代-复制算法
  • 老年代-标记清除算法
  • 老年代-标记整理算法
  • 永久代-方法区回收

年轻代-复制算法

该算法的核心是将可用内存按容量划分为大小相等的两块,每次只用其中一块,当这一块的内存用完,就将还存活的对象复制到另外一块上面,然后把已使用过的内存空间一次清理掉。这使得每次只对其中一块内存进行回收,分配也就不用考虑内存碎片等复杂情况,实现简单且运行高效。如下图:

老年代-标记清除算法

该算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象(可达性分析),在标记完成后统一清理掉所有被标记的对象。如下图:

该算法会有以下两个问题:

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

老年代-标记整理算法

标记清除算法会产生内存碎片,而复制算法需要有额外的内存担保空间,于是针对老年代的特点,又有了标记整理算法。标记整理算法的标记过程与标记清除算法相同,但后续步骤不再对可回收对象直接清理,而是让所有存活的对象都向一端移动,然后清理掉这一端边界以外的内存。如下图:

永久代-方法区回收

在方法区进行垃圾回收一般“性价比”较低,因为在方法区主要回收两部分内容:废弃常量 和 无用的类 。

回收废弃常量与回收其他年代中的对象类似。

但是判断一个类是不是无用的类的条件则相当苛刻:

  • 该类所有的实例都已经被回收,Java堆中不存在该类的任何实例;
  • 该类对应的Class对象没有在任何地方被引用;
  • 加载该类的ClassLoader已经被回收;

但即使满足以上条件也未必一定会回收,Hotspot VM还提供了-Xnoclassgc参数控制(关闭CLASS的垃圾回收功能)。因此在大量使用动态代理、CGLib等字节码框架的应用中一定要关闭该选项,开启VM的类卸载功能,以保证方法区不会溢出。

4、垃圾回收的两个重要方法

System.gc()方法 和 finalize()方法

System.gc()方法

使用System.gc() 可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。在命令行中有一个参数-verbosegc可以查看Java使用的堆内存的情况,它的格式如下:java -verbosegc classfile 由于这种方法会影响系统性能,不推荐使用,所以不详细介绍。

finalize()方法

JVM垃圾回收器在回收一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止该对象从而释放资源,这个方法就是finalize() 。它的原型为:protected void finalize() throws Throwable,在finalize() 方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable 表示它可以抛出任何类型的异常。

之所以要使用finalize(),是因为存在着垃圾回收器不能处理的特殊情况。例如:

  1. 由于在分配内存的时候可能采用了类似 C语言的做法,而非Java通常的new 。这种情况主要发生在 native 方法中,比如 native 方法调用了C/C++malloc()函数来分配存储空间,除非调用 free() 函数,否则这些内存空间将不会得到释放,那么这个时候就可能造成内存泄漏。但是由于 free() 是C/C++中的函数,所以 finalize() 中可以用本地方法来调用它。以释放这些“特殊”的内存空间。

  2. 或者是打开的文件资源,这些资源不属于垃圾回收器的回收范围。

5、触发GC(Garbage Collector)的条件

  1. GC在优先级最低的线程中运行,一般在应用程序空闲即没有应用线程在运行时被调用。但下面的条件例外。

  2. Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制调用GC线程。若GC一次之后仍不能满足内存分配,JVM会再进行两次GC,若仍无法满足要求,则JVM将报“out of memory”的错误,Java应用将停止。

6、减少GC开销的措施

  1. 不要显式调用System.gc() 。此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。大大的影响系统性能。

  2. 尽量减少临时对象的使用。临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。

  3. 对象不用时最好显式置为Null。一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

  4. 尽量使用StringBuffer,而不用String来累加字符串。由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如 Str5=Str1+Str2+Str3+Str4; 这条语句执行过程中会产生多个垃圾对象,因为每次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。

  5. 能用基本类型如 int, long 就不用包装类型Integer, Long。基本类型变量占用的内存资源比包装类型占用的少得多,如果没有必要,最好使用基本变量。

  6. 尽量少用静态对象变量。静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

  7. 分散对象创建或删除的时间。集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。

7、几种垃圾收集器

在JDK7中,有5种垃圾收集器:

  • Serial收集器
  • Parallel收集器
  • Parallel Old收集器 (Parallel Compacting GC)收集器
  • Concurrent Mark & Sweep GC (or “CMS”)收集器
  • Garbage First (G1) 收集器

其中,Serial 收集器一定不能用于服务器端。这个收集器类型仅应用于单核CPU桌面电脑。使用Serial收集器会显着降低应用程序的性能。

转载自:

03 JVM的垃圾回收机制

JVM虚拟机—JVM的垃圾回收机制(转载)的更多相关文章

  1. JVM架构和GC垃圾回收机制

    深入理解系列之JDK8下JVM虚拟机(1)——JVM内存组成 https://blog.csdn.net/u011552404/article/details/80306316 JVM架构和GC垃圾回 ...

  2. java面试题之----JVM架构和GC垃圾回收机制详解

    JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...

  3. JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)

    转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...

  4. JVM内存分配与垃圾回收机制管理

    项目上线,性能优化有个重要组成就是jvm内存分配和垃圾回收机制的管理配置. 网上随便能搜到相关的具体步骤,以及内存中各种参数对应的意义,不再赘述. 干货就是直接抛出遇到的问题,以及如何解决的,再说说待 ...

  5. JVM构架、GC垃圾回收机制的理解

     JVM是Java Virtual Machine(Java虚拟机)的缩写 1.程序计数器 它的作用可以看做是当前线程所执行的字节码的行号指示器. 每个线程都有一个程序计算器,就是一个指针,指向方法区 ...

  6. 【Java_基础】JVM内存模型与垃圾回收机制

    1. JVM内存模型 Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间. JVM内存模型如下图所示 1.1 程序计数器 程序计数器( ...

  7. JVM内存管理和垃圾回收机制介绍

    http://backend.blog.163.com/blog/static/20229412620128233285220/     内存管理和垃圾回收机制是JVM最核心的两个组成部分,对其内部实 ...

  8. JVM内存模型及垃圾回收机制

    http://blog.csdn.net/zhangpengju999/article/details/11773183 JVM垃圾回收机制 分代垃圾回收 不同的对象生命周期不同.与业务信息有关的对象 ...

  9. php 垃圾回收机制 转载

    PHP的基本GC概念PHP语言同其他语言一样,具有垃圾回收机制.那么今天我们要为大家讲解的内容就是关于PHP垃圾回收机制的相关问题.希望对大家有所帮助.PHP strtotime应用经验之谈PHP m ...

  10. JVM架构和GC垃圾回收机制详解

    JVM架构图分析 下图:参考网络+书籍,如有侵权请见谅 (想了解Hadoop内存溢出请看:Hadoop内存溢出(OOM)分类.参数调优化) JVM被分为三个主要的子系统 (1)类加载器子系统(2)运行 ...

随机推荐

  1. Jquery学习笔记(9)--注册验证复习(未用到ajax)

    纯复习,在$(this).val()这里浪费了时间,val()只适合input里面的value值,如果是span等标签里包裹的文本要用text()!! <!DOCTYPE html> &l ...

  2. linux学习笔记11---命令more

    more命令,功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上. more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会 ...

  3. ext,exrReturn新增,修改删除等用

    package cn.edu.hbcf.common.vo; /** * Ext Ajax 返回对象 * * @author * @date 2012-02-21 19:30:00 * */ publ ...

  4. LCD屏参数及应用举例

     1.  LCD参数及原理 R G B 信号 PCLK(像素时钟),LCLK(HSYNC,线时钟,水平同步时钟),FCLK(VSYNC,帧时钟,垂直同步时钟) 7寸屏一般由两种工作模式DE和时钟模式, ...

  5. IO模型(阻塞、非阻塞、多路复用与异步)

    IO模型 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同环境下给出的答案也是不一的.所以先限定一下上下文是非常有必要的. 本文讨论的背景是Linux环境下的network I ...

  6. js控制button

    按钮变灰不可用方法:document.getElementById("crop").setAttribute("disabled", true); 按钮可用方法 ...

  7. Redmin 一键安装

    一.介绍 Redmine是用Ruby开发的基于web的项目管理软件,是用ROR框架开发的一套跨平台项目管理系统,据说是源于Basecamp的ror版而来,支持多种数据库,有不少自己独特的功能,例如提供 ...

  8. yii2 basic 简洁安装教程

    虽然yii官网上说叫咱们用composer安装来安装,但是鄙人感觉这样的安装方式会将自己写的代码共享到github中,因此不太建议大家这样用.下面来给大家介绍basic简洁的安装方法. 第一步,下载b ...

  9. iOS项目开发实战——自己定义圆形进度提示控件

    iOS中默认的进度条是水平方向的进度条,这往往不能满足我们的需求. 可是我们能够自己定义类似的圆形的进度提示控件,主要使用iOS中的画图机制来实现. 这里我们要实现一个通过button点击然后圆形进度 ...

  10. win7下安装memcached

    memcached server端服务在win7下的安装.启动图解 1.首先下载解压memcached-1.2.6-win32-bin.zip到某一盘下,如下图 2.通过管理员方式运行cmd.exe. ...