大数据学习-Java JVM垃圾回收机制

一、概述:

本文作为大数据基础的Java部分,上一篇说明了Linux的常用命令,今天学习了Java的高级特性,我们关注与Java的内存管理的机制,也就是垃圾是如何回收的俗称GC问题,这里用《深入理解Java虚拟机》的要解决的问题作为概述,解决三个问题:

  • 哪些内存需要回收?

    线程公有的部分,一般是Java堆 和方法区 ,两个算法判断是否需要回收 引用计数法可达性分析算法(也称跟搜索法)

  • 什么时候回收?

    Java堆是分不同的类,比如新生代,老年代,永久代是有不同的分类算法和时机进行回收

  • 如何回收?

    运用JVM中不同的垃圾回收策略

    • 标记-清除算法
    • 复制算法
    • 标记整理算法
    • 分代收集算法(解决什么时候回收的问题)

二、哪些内存需要回收?

先看一下基本的JVM内存模型,五种基本内存模型,并做一下简单概述:

五种基本Java内存模型

两种类型:

  • 所有线程共享的数据区:
    1. 方法区: 存储已被虚拟机加载的类信息、常量、静态变量、即时编译后代码等数据。常量池位于方法区,并使用永久代来实现方法区
    2. Java堆区: 我们常说用于存放对象的区域
  • 线程私有(隔离)数据区:
    1. 虚拟机栈: 方法执行时创建一个栈帧,用于存储局部变量、操作数栈、动态链接、方法出口等信息。每个方法一个栈帧,互不干扰
    2. 本地方法栈: 用于存放执行native方法的运行数据
    3. 程序计数器: 当前线程所执行的字节码的指示器,通过改变计数器来选取下一条需要执行的字节码指令

我们基本是要回收Java堆区,因此也称之为GC堆,下面用两个算法来表示到底什么才是需要回收的对象

  1. 引用计数法:计算对象的引用数量

    引用计数算法是通过计算对象的引用数量来决定对象是否可以被回收。

    引用计数算法是垃圾收集器中的早期策略。在这种方法中,堆中的每个对象实例都有一个引用计数。当一个对象被创建时,且将该对象实例分配给一个引用变量,该对象实例的引用计数设置为 1。当任何其它变量被赋值为这个对象的引用时,对象实例的引用计数加 1(a = b,则b引用的对象实例的计数器加 1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数减 1。特别地,当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器均减 1。任何引用计数为0的对象实例可以被当作垃圾收集。

    但是也有很多问题,如果两个对象循环相互引用,而实例化的对象引用已经为null,对象却有引用,永远无法回收,简单代码如下:

    public class ReferenceCountingGC {
      
            public Object instance = null;
    
            public static void testGC(){
    
                ReferenceCountingGC objectA = new ReferenceCountingGC ();
                ReferenceCountingGC objectB = new ReferenceCountingGC ();
    
                // 对象之间相互循环引用,对象objA和objB之间的引用计数永远不可能为 0
                objectA.instance = objectB;
                objectB.instance = objectA;
    
                 //虽然实例化的对象已经为null,但是因为objectA一直被引用,所以引用数量不为0
                objectA = null;
                objectB = null;
    
                System.gc();
        }
    }
    

  2. 可达性分析算法(根搜索法):引用图论判断对象是否可达

    可达性分析算法(根搜索法)是通过计算对象的引用链是否可达来决定对象是否可以被回收。

    可达性分析算法(根搜索法)是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,通过一系列的名为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的

三、如何回收以及什么时候回收?

JVM中常见的垃圾回收策略:标记-清除算法、复制算法、标记-整理算法、分代回收算法

  1. 标记-清除算法

    标记-清除算法分为标记和清除两个阶段。该算法首先从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象并进行回收,如下图所示。

    标记-清除算法

从图中能看到,每次要扫描内存,标记,再扫描并且清除相应的内存块,缺点也就相应展示出来了,效率并不高效,需要进行扫描标记再扫描进行删除,另外空间会产生非常多的不连续碎片,会造成大对象的分配时进行二次扫描。

  1. 复制算法

    为了解决效率问题,复制算法应运而生复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这种算法适用于对象存活率低的场景,比如新生代。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。该算法示意图如下所示:

    复制算法

事实上,现在商用的虚拟机都采用这种算法来回收新生代。因为IBM公司研究发现,新生代中的对象是”朝生夕死“,所以需要复制的对象很少,效率还不错。实践中会将新生代内存分为一块较大的Eden空间和两块较小的Survivor空间 (如下图所示),每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是 8:1,也就是每次新生代中可用内存空间为整个新生代容量的90% ( 80%+10% ),只有10% 的内存会被“浪费”。

—摘自深入了解Java虚拟机

  1. 标记-整理算法

    复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。标记整理算法的标记过程类似标记清除算法,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,类似于磁盘整理的过程,该垃圾回收算法适用于对象存活率高的场景(老年代),其作用原理如下图所示。

标记-整理算法

标记整理算法与标记清除算法最显著的区别是:标记清除算法不进行对象的移动,并且仅对不存活的对象进行处理;而标记整理算法会将所有的存活对象移动到一端,并对不存活对象进行处理,因此其不会产生内存碎片。标记整理算法的作用示意图如下:

标记整理算法的作用
  1. 分代收集算法(解决什么时候回收的问题)

    对于一个大型的系统,当创建的对象和方法变量比较多时,堆内存中的对象也会比较多,如果逐一分析对象是否该回收,那么势必造成效率低下。分代收集算法是基于这样一个事实:不同的对象的生命周期(存活情况)是不一样的,而不同生命周期的对象位于堆中不同的区域,因此对堆内存不同区域采用不同的策略进行回收可以提高 JVM 的执行效率。当代商用虚拟机使用的都是分代收集算法:新生代对象存活率低,就采用复制算法;老年代存活率高,就用标记清除算法或者标记整理算法。Java堆内存一般可以分为新生代、老年代和永久代三个模块,如下图所示:

    Java堆内存的三代世家

    • 新生代(Young Generation)

    新生代的目标就是尽可能快速的收集掉那些生命周期短的对象,一般情况下,所有新生成的对象首先都是放在新生代的。新生代内存按照 8:1:1 的比例分为一个eden区和两个survivor(survivor0,survivor1)区,大部分对象在Eden区中生成。在进行垃圾回收时,先将eden区存活对象复制到survivor0区,然后清空eden区,当这个survivor0区也满了时,则将eden区和survivor0区存活对象复制到survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后交换survivor0区和survivor1区的角色(即下次垃圾回收时会扫描Eden区和survivor1区),即保持survivor0区为空,如此往复。特别地,当survivor1区也不足以存放eden区和survivor0区的存活对象时,就将存活对象直接存放到老年代。如果老年代也满了,就会触发一次FullGC,也就是新生代、老年代都进行回收。注意,新生代发生的GC也叫做MinorGC,MinorGC发生频率比较高,不一定等 Eden区满了才触发。

    • 老年代(Old Generation)

    老年代存放的都是一些生命周期较长的对象,就像上面所叙述的那样,在新生代中经历了N次垃圾回收后仍然存活的对象就会被放到老年代中。此外,老年代的内存也比新生代大很多(大概比例是1:2),当老年代满时会触发Major GC(Full GC),老年代对象存活时间比较长,因此FullGC发生的频率比较低。

    • 永久代(Permanent Generation

    永久代主要用于存放静态文件,如Java类、方法等。永久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如使用反射、动态代理、CGLib等bytecode框架时,在这种时候需要设置一个比较大的永久代空间来存放这些运行过程中新增的类。

  2. 结论

    • 标记-清除算法:会存在效率和内存碎片问题
    • 复制算法:为了解决标记清除的问题,但是会浪费空间,进行分区复制回收,适用于新生代
    • 标记-整理算法:为了解决复制算法空间浪费的问题,进行标记清除并且整理,适用于老年代
    • 分代回收算法:解决合适的时间去回收内存,根据对象的存活时间进行分类
      • 老年代:标记-清理/标记-整理算法
      • 新生代:复制算法
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供...
    简欲明心阅读 88,652评论 17 311
  • JVM架构 当一个程序启动之前,它的class会被类装载器装入方法区(Permanent区),执行引擎读取方法区的...
    cocohaifang阅读 1,576评论 0 7
  • 来自: Android梦想特工队作者: Aaron主页: http://www.wxtlife.com/原...
    技术特工队阅读 4,288评论 0 28
  • 如果你想要从太空观察地球,卫星技术就能够做到这一点。图中的海洋流是卫星地图展现的,紫色和粉红色的漩涡代表更暖的洋流...
    allever阅读 1,349评论 0 8
  • 从收走叶子的水分开始 一把叶柄支撑着秋的隐喻 风在天上快乐地吹口哨 叶子被剥夺了哭泣的权力,最后的泪 从身体里剥离...
    鶑鵅阅读 130评论 0 0