GC回收大法

        上一篇小文章总结了一下JVM,今天就来给大家捋一捋JVM里经常被问的GC机制吧,走过路过别错过,千山万水总是情,大哥大姐留个赞,谢谢。下面就进入正题吧,宏观回顾JVM内存模型:

一、JVM内存模型

内存模型

        从大的方面来讲,JVM内存有非堆内存和堆内存之分,也就是大家口中所说的永久区内存(permanent space)和堆内存(heap space)。

        栈内存一般都不归于JVM内存模型中,因为栈内存属于线程私有。永久区内存存的是class类级对象如class本身的信息,method,field等等;堆内存存储的是对象实例和数组。

        如上图所示,heap space由Old Generation(年老区)和Young Generation(年轻区)组成。Old Generation(年老区)存放的对象一般是一些存活周期比较长的对象,然后一般新创建的对象会先存放在年轻区,但是也有个例,年老区有个对象保护机制,就是当新建的对象在年轻区(该年轻区是刚经历过GC后的)放不下时,会启动临时保护机制,帮忙先存放一下新创建的大对象。JVM有-XX:PretenureSizeThreshold参数,大于这个参数值的对象将直接分配到老年代中。Young Generation(年轻区)还根据8:1:1的比例分为了Eden区、survivor0区和survivor1区,新建的对象一般会放在Eden区,当Eden区内存不足,会GC一次把Eden区的对象复制到survivor区,survivor区内存不足也会把对象复制到old区,当所有区可使用内存大小都不足以新建对象,就会抛出OOM(内存溢出)了。JVM新建对象的过程如下:


对象新建分配房子

        在这稍微提一下,在JAVA1.7版本之前,永久区就是我们说的方法区,永久区内存才能溢出相对少见,一般加载海量class数据,超过了非堆内存容量导致。通常是发生在web应用刚加载运行时,所以推荐web都使用预加载机制,方便在部署项目时就发现并解决问题。然而在JAVA1.8版本后,永久区被移除了,更名为元空间,也就是之前存在方法区或者说是永久区的数据会存在本地内存里,本地物理机内存会相对比jvm虚拟机内存大,,但也只是减少了OOM的出现,,过大使用内存还是会OOM的。至于栈内存溢出,更少见,不过呢,如果是递归调用方法进入死循环的话,就会出现栈内存溢出了。

堆内存优化:调整JVM启动参数-Xms  -Xmx   -XX:newSize -XX:MaxNewSize,如调整初始堆内存和最大堆内存 -Xms256M -Xmx512M。 或者调整初始New Generation的初始内存和最大内存 -XX:newSize=128M -XX:MaxNewSize=128M。 

永久区优化:调整PermSize参数   如  -XX:PermSize=256M -XX:MaxPermSize=512M。

栈内存优化:调整每个线程的栈内存容量  如  -Xss2048K 。

        总结一下,一个运行中的JVM所占的内存= 堆内存  +  永久区内存  +  所有线程所占的栈内存总和 。不过呢,有一句话还是要提一下的,不要为了调优而调优,JVM的堆内存新老年代空间分配不要随意改变,会影响一些垃圾收集器自带的回收性能。

二、垃圾回收机制

1、范围:垃圾回收区域

        java虚拟机栈、本地方法栈以及PC寄存器都是线程私有的,随着线程的创建和消亡,会自动被GC机制回收的。然而方法区和java堆是GC重要回收区域,毕竟一个接口可能会有多个实现类,多个实现类会被分配的内存也不同,一个方法多个分支的执行也有被分配不同的内存,而且这些对象或者实例随时会不再被引用的,因此需要GC进行动态回收才能保证JVM不会那么容易出现OOM。

2、前提:如何判断对象可被回收

        ①引用计数法

        通俗说法就是通过一个计数器来计算对象的引用次数,初始化数值是0,当对象被引用就+1,不被引用时就计数器-1,当GC回收时,会把数值为0的回收掉;不过此方法虽然简单,但是呢解决不了循环引用的BUG,比如对象A包含指向对象B的引用,对象B也包含指向对象A的引用,但没有引用指向A和B,运用引用计数法来回收的话,A和B的引用都是1,不会被回收,会出现内存泄露问题了。

        ②根搜索


根搜索

        根搜索又称之为可达性分析法,通过选取一个GC Root(根对象)作为起始点,然后向下搜索,如果有哪个对象是无法连接到GC Root的话,便判断为不可达对象,也就是该对象没有被引用,可以作为根的对象有:栈中变量引用的对象,类静态属性引用的对象,常量引用的对象等。因为每个线程都有一个栈,所以我们需要选取多个根对象。不过呢,被根搜索第一次找到的对象,不会立马被回收,而是被标记后丢到F-queue队列里等到finalize()方法执行,看看是否会复活对象,如果不被复活,那在第二次GC时会被回收掉了。需要注意的地方,第一,finalize()方法只会执行一次,也就是对象只有一次复活机会,第二,执行GC后,要停顿半秒等待优先级很低的finalize()执行完毕。

3、策略:GC回收算法

        ①标记-清除法

        顾名思义,标记清除法就是两步,第一步,先找到不被引用的对象,然后进行标记,第二步就是进行清除回收对象。该算法毕竟容易实现,但是呢会产生不连续内存碎片,这样子,如果有大对象新建时,很容易会出现内存不足,然而进行又一次的GC,很消耗CPU性能哦。

        ②标记-复制法

        一开始就是把内存分为两部分,当进行完GC后,把还存活的对象复制到另一部分空闲内存空间,该算法实现也很简单,当大部分对象都被回收时这种策略也很高效。但也有一个缺点,可用内存减少了一半。怎么办呢,聪明人做法,不按照1:1平分,而是按照8:1:1分为一个Eden区和两个survivor区,每次将Eden和Survivor中存活的对象复制到另一块空闲的Survivor中。该三个区域组成了新生代。

        ③标记-整理法

        根据老年代的特点,采用回收掉垃圾对象后对内存进行整理的策略再合适不过,将所有存活下来的对象都向一端移动。

4、实现:垃圾收集器


垃圾收集器分类

        上图已经很详细了,但是我简单say一下可用来回答面试官吧。

        Serial,单线程串行收集器,应用于新生代垃圾回收,使用标记-复制法,执行期间需要STW暂停业务流程。

        Serial Old,Serial的老年代版本,应用于老年代垃圾回收,主用于JVM的client回收,使用标记-整理法,执行期间需要STW暂停业务流程。

        ParNew,多线程并行收集器,应用于新生代垃圾回收,使用标记-复制法,适用于server模式下,执行期间需要STW暂停业务流程。

        Parallel Scavenge,多线程并行收集器,应用于新生代垃圾回收,使用标记-复制法,但是更注重于吞吐量,可精确控制。

        Parallel Old, Parallel Scavenge的老年代版本,应用于老年代垃圾回收,使用标记-整理法,优先适用于吞吐量优先的环境下。


CMS收集器

        CMS,多线程并发收集器,应用于老年代回收,使用的是标记-清除法,优点是低停顿,官方文档说法是无论多少垃圾回收都可保证停顿时间是10ms,缺点会产生内存碎片,可以使用-XX:CMSFullGCsBeforeCompaction设置执行几次CMS回收后,跟着来一次内存碎片整理。


G1收集器

        G1收集器,是CMS终极改进版,充分利用多CPU、多核进行并行和并发收集,大大缩短停顿时间,还可预测停顿时间避免应用雪崩现象,把年老和新生代区改变成大小一样的region来管理,内存使用更灵活,避免了内存碎片问题。

5、触发:何时GC回收

        GC回收分类有三种,新生代回收称之为minor GC,老年代回收称之为major GC,System.gc的总回收称之为Full GC。Full GC触发条件有:①Old空间不足 ;②统计得到的Minor GC(默认是15次GC后)晋升到老年代的平均大小大于老年代的剩余空间;③永久区内存不足,不过该永久区不等同于方法区,只是堆内存自实现的一个方法区。值得一提,不同JVM对象的引用,GC也不同的:

强引用:就是类似 object obj  = new object(),这样新建对象都是强引用,该实例没有对象引用时才会被回收。

软引用:适用于缓存场景,只有内存不够用才会被GC。

弱引用:在GC时一定会被回收。

虚引用:一般是用来确认对象是否被GC了。

三、JVM性能优化


JVM性能优化

        此处有个小彩蛋,穿插一下JVM性能优化的简单总结。

        到此,GC的简单入门到入魔大法已经完成了。多学多记录。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352