垃圾(GC)回收机制及算法以及配置调优

 

目录

概述

1.产生垃圾的地方

2.垃圾回收过程

3.内存

1.如何判断一个对象是否可以被回收?

1.1 引用计数法

1.2 可达性分析算法

2.垃圾收集算法

2.1 标记-清除算法(Mark-Sweep)

2.2 复制算法(Copying)

2.3 标记-整理算法(Mark-Compact)——老生代

2.4 分代收集算法(Generational Collection)

3. GC收集器

3.1 Serial收集器 == Stop The World—-新生代

3.2 ParNew收集器——新生代

3.3 CMS(Concurrent Mark Sweep)

    设计思想

3.4 Parallel

3.5 G1GC

4. GC配置

4.1 常见的4种GC

4.2 一些配置解释


概述


1.产生垃圾的地方

  栈---变量、方法、代码块 。会产生无用内容,但会立即清除
  方法区---存储类的信息 
  堆---存储的对象,对象使用完成之后(无用对象)会在某个时间进行回收

2.垃圾回收过程

    在程序启动的时候就会开始监测堆内存的使用情况,监测如果堆内存的使用率超过了一个临界值(0.75)就开始通知GC(Garbage Collector)进行垃圾回收,GC会在某个时间段进行垃圾回收----System.gc();---通知
    垃圾分代回收机制:

3.内存

 

  1. 内存分成新生代(伊甸园区和幸存区)和老生代,新创建的对象会存储在伊甸园区,此时系统会对伊甸园区进行一次扫描,如果这个对象没有在使用就会被通知GC进行回收,如果还在使用就会把这个对象挪到幸存区,系统会对这个对象进行多次扫描,如果这个对象没有在使用就会通知GC进行回收,如果还在使用就会把这个对象挪到老生代,系统会对这个对象进行多次扫描(扫描频率比幸存区的频率低),如果这个对象没有在使用就会通知GC进行回收,如果还是使用就继续待在老生代,重复上述操作   
  2. 如果老生代的的对象突然消失--系统可能崩溃
  3. 如果刚创建的对象的内存很大,伊甸园区存储不了,会挪到老生代进行存储,如果老生代也存储不了,就会报错OutOfMemoryError
     

1.如何判断一个对象是否可以被回收?


1.1 引用计数法

通过判断对象的引用计数器是否为0,如果为0 ,则可以被回收
    优点:简单高效
    缺点:不能解决对象之间循环引用问题,肯造成内存泄漏
    a=b,b=a.这两对象的引用计数器永远不为0,会导致对象清除不掉,造成内存泄漏\

1.2 可达性分析算法

判断GC ROOT是否有相连的引用链,如果没有就可以被回收,解决对象之间循环引用的弊端

2.垃圾收集算法

2.1 标记-清除算法(Mark-Sweep)

  1. 是最进本的回收算法,
  2. 优点:简单,便于实现,
  3. 缺点;会产生大量的内存碎片,造成内存环境质量下降,影响程序运行性能(在分配较大对象时,如果没有足够的连续内存地址空间,就会不得不触发额外的GC,)

2.2 复制算法(Copying)

  1. 将存活对象copy到另外一个块内存中,再将数据一次性清理掉
  2. 适用场景:新生代
  3. 优点:避免产生内存碎片
  4. 缺点:内存利用率低,50%内存利用率
  5.  改进:8:1:1(新生代中的对象98%是“朝生夕死”的)

     

    回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间
     内存利用率90%
     98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。
    内存的分配担保指的是:如果另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。此外,对象进入老年代还有另外一个触发条件,就是一个对象在两个Survivor间进行过多次的移动。
    新生代的对象如果达到提升临界值(15次),可以晋升到老生代,老生代的对象都是常用对象
相关概念:
    新生代GC:Minor GC,频率较高
    老生代GC:Marjor GC(Full GC)——全堆GC,频率较低
    GC调优的目的:不希望发生Full GC或者延迟Full GC的到来,因为一旦发生Full GC,速度会很慢

2.3 标记-整理算法(Mark-Compact)——老生代

2.4 分代收集算法(Generational Collection)

    一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
    在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

3. GC收集器

    7种作用于不同分代的收集器
    连线表示两个收集器可以配合使用,不连线,说明不兼容。

3.1 Serial收集器 == Stop The World—-新生代

Serial收集器是最基本、发展历史最悠久的收集器。是一个单线程的收集器,它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束、(任何收集器在收垃圾的时候都会产生停顿,只是有的时间短,有的时间长)
    你妈妈在给你打扫房间的时候,肯定也会让你老老实实地在椅子上或者房间外待着,如果她一边打扫,你一边乱扔纸屑,这房间还能打扫完?

3.2 ParNew收集器——新生代

    ParNew收集器其实就是Serial收集器的多线程版本,目前除了Serial收集器外,目前只有ParNew能与CMS收集器配合工作。

3.3 CMS(Concurrent Mark Sweep)

基于“标记—清除”算法实现的——老生代

    并发低停顿收集器,停顿时间非常短,用户几乎感觉不到,一般用于和用户交互比较频繁的系统。B/S,非常注重响应速度。比如:Hbase(低延迟的数据查询),底层默认用的是CMS

    设计思想:

整个过程分为4个步骤,包括:

  1. 初始标记(CMS initial mark) 会产生停顿,非常短,仅是扫描引用链
  2. 并发标记(CMS concurrent mark)允许与工作线程(用户线程)一起工作
  3. 重新标记(CMS remark) 会产生停顿,修正并发标记引起的引用链变化
  4. 并发清除(CMS concurrent sweep)清除垃圾,和用户线程一起工作

    把停顿时间最长的清除阶段设计可以和工作线程并发处理,即清除垃圾时没有停顿。所以可以极大的降低停顿时间
    优点:停顿时间最短,对用户体验较好
    缺点:在清除垃圾阶段,垃圾收集线程和用户线程会共同抢占cpu时间片,可能会产生一定的性能下降;CMS可能会产生一些浮动垃圾(边扫屋子,边扔垃圾),浮动垃圾只有等下次GC才会收掉
    CMS是基于“标记—清除”算法实现的,所以在收集垃圾时会产生内存碎片,所以需要参数配置,收集到一定次数后整理内存空间避免内存过多的问题

3.4 Parallel

吞吐量优先收集器,并不关注回收的停顿时间,而是关注回收的吞吐量,对于交互延迟不高的框架可以使用,比如:HDFS底层可以使用Parallel。

3.5 G1GC

是Java最新的垃圾收集器,即兼顾垃圾回收的吞吐量,又能确保回收的低延迟,


    设计思想:抛弃传统的GC的收集器做法(我们把Heap空间分为Young/Old两个分区,Young分区又包括一个Eden和两个Survivor分区,如下图所示。新产生的对象首先会被存放在Eden区,而每次minor GC发生时,JVM一方面将Eden分区内存活的对象拷贝到一个空的Survivor分区,另一方面将另一个正在被使用的Survivor分区中的存活对象也拷贝到空的Survivor分区内。)
    G1GC:它将整个Heap分为若干个预先设定的小区域块(1MB~32MB,大小可调),每个区域块内部不再进行新旧分区, 而是将整个区域块标记为Eden/Survivor/Old。当创建新对象时,它首先被存放到某一个可用区块(Region)中。当该区块满了,JVM就会创建新的区块存放对象。当发生minor GC时,JVM将一个或几个区块中存活的对象拷贝到一个新的区块中,并在空余的空间中选择几个全新区块作为新的Eden分区。当所有区域中都有存活对象,找不到全空区块时,才发生Full GC。即G1 GC发生Full GC的频次要比其他GC更低,因为内存使用率很高。
    而在标记存活对象时,G1使用RememberSet的概念,将每个分区外指向分区内的引用记录在该分区的RememberSet中,避免了对整个Heap的扫描,使得各个分区的GC更加独立。
    优点:1.对于内存较大的环境非常友好。因为G1 GC对于内存的使用率特别高,内存越大,此优势越明显。2.当整个Heap没有全空的区域块时,才发生Full GC。

4. GC配置

4.1 常见的4种GC


1. SerialGC 
参数-XX:+UseSerialGC   #就是Young区和old区都使用serial 垃圾回收算法
2. ParallelGC 
参数-XX:+UseParallelGC
Young区:使用Parallel scavenge 回收算法
Old 区:可以使用单线程的或者Parallel 垃圾回收算法,由 -XX:+UseParallelOldGC 来控制
3. CMS 
参数-XX:+UseConcMarkSweepGC
Young区:可以使用普通的或者parallel 垃圾回收算法,由参数 -XX:+UseParNewGC来控制
Old 区:只能使用Concurrent Mark Sweep 
4. G1 
参数:-XX:+UseG1GC  #没有young/old区

4.2 一些配置解释

-XX:+UseG1GC    使用 G1 (Garbage First) 垃圾收集器
-XX:MaxGCPauseMillis=n    设置最大GC停顿时间(GC pause time)指标(target). 这是一个软性指标(soft goal), JVM 会尽量去达成这个目标.
-XX:InitiatingHeapOccupancyPercent=n    启动并发GC周期时的堆内存占用百分比. G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比. 值为 0 则表示"一直执行GC循环". 默认值为 45.
这个参数是一个常用的GC调优参数,上面的百分比控制的Minor GC的百分比
可以适当调低此参数,提高MinorGC的频次,从而延迟FullGC的到来
-XX:NewRatio=n    新生代与老生代(old/new generation)的大小比例(Ratio). 默认值为 2.
-XX:SurvivorRatio=n       eden/survivor 空间大小的比例(Ratio). 默认值为 8.
-XX:MaxTenuringThreshold=n    提升老年代的最大临界值(tenuring threshold). 默认值为 15.
-XX:ParallelGCThreads=n    设置垃圾收集器在并行阶段使用的线程数,默认值随JVM运行的平台不同而不同.一般设置的数量=服务器的核数
-XX:ConcGCThreads=n    并发垃圾收集器使用的线程数量. 默认值随JVM运行的平台不同而不同(调节CMS)
-XX:G1ReservePercent=n    设置堆内存保留为假天花板的总量,以降低提升失败的可能性. 默认值是 10.
假天花板的概念:比如可用内存100GB,假天花板是10%,意味着G1最多能用90GB,预留出10GB,从而降低失败的可能性,
-XX:G1HeapRegionSize=n    使用G1时Java堆会被分为大小统一的的区(region)。此参数可以指定每个heap区的大小. 默认值将根据 heap size 算出最优解. 最小值为 1Mb, 最大值为 32Mb.
 


如果您看到这了,请在右上角给点个赞鼓励一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值