zoukankan      html  css  js  c++  java
  • 深入研究Java GC

    2019/4/2 星期二
    深入研究java gc
    引出问题和小结!
    小结:
    1、为什么使用CMS gc回收算法?
    //答:
    因为在CMS gc算法执行的6个步骤中,只有在第一步(初始标记(STW Initial Mark))和第四步重新标记阶段(STW REMARK)才会暂停整个应用,这样对应用程序所带来的影响非常小,缺点是产生内存碎片过多
    2、那CMS GC策略如何导致内存碎片过多?
    //答:是因为第二步并发标记(concurrent marking)与回收线程会与应用程序争抢CPU资源,容易产生内存碎片,其二:CMS算法在标记清理之后并没有重新压缩分配存活对象,因此整个老生代会产生很多的内存碎片。
    3、那为什么CMS gc策略会耗时比较长呢?
    //答:
    ‘stop-the-world’暂停时间也很短暂,耗时较长的(第二步并发标记)标记和(第三步并发预清理)清理都是并发执行的。

    内存碎片过多如何触发Full GC?
    //答:
    CMS并不是很完美,它会在两种场景下产生严重的Full GC(Concurrent Failure(并发失败),Promotion Failure (促销失败))
    具体见:老年代 CMS gc回收算法 对hbase的影响 https://blog.51cto.com/12445535/2373206

    HBase在演进的道路上又如何不断优化CMS GC?

    具体内容见下详细介绍:


    题外话:什么是java程序的执行流程;java运行时数据区;java的内存管理 见如下图:
    java程序执行流程:
    深入研究java gc

    java运行时数据区:
    深入研究java gc

    java的内存管理:
    深入研究java gc

    在我们(运行时数据区)之中,内存的分配一共有五块:
    1、堆内存(Heap):保存真正的程序的数据的部分;
    2、&&&栈内存(Stack):保存堆内存地址、还保存基本数据、方法的执行;(所有的数据都在栈内存之中)
    3、方法区:保存所有方法的具体的操作,该区域属于共享;
    4、程序计数器:这是一块很小的内存,小到几乎可以忽略的地步,只是做一个程序执行顺序的记录,只是为了标记我们下一步要执行的代码的顺序号;
    5、本地方法栈:该栈之中所保存的都是操作系统的原生函数。
    我们关心的主要是堆内存、栈内存、方法区

    在整个的JVM内存组成过程之中,(栈内存)是一个非常重要的概念,因为在该内存之中,他需要保存的数据是一组内容,

    因为所有的方法在进行递归调用的时候都会采用栈的模式。观察递归问题中满栈的原因取决于服务器内存的大小。

    内存操作有关的两类异常
    stackOverFlowError(栈溢出):如果请求的栈的深度过大,虚拟机可能会抛处。
    OutOfMemoryError(内存溢出):如果虚拟机的实现中允许、虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出。【内存被沾满,更多情况下表示堆也分配不了了】

    实际上上面只是观察到了两类可能出现错误的代码,但是并不是意味着栈中只能够保存一下基本的信息,实际上栈里面保存同样是一组的数据。

    总结:
    1、造成stackOverFlowError(栈溢出)OutOfMemoryError(内存溢出)的原因是;
    2、在JVM栈内存中保存有栈幁的概念,所有的栈内存采用先进后出的数据结构来进行我们的存储。

    首先需要了解一下什么是java的堆内内存划分
    在实际情况下:java 堆内存划分分为了(jdk1.8以前和jdk1.8之后)【对于这2者的区别,我们后面介绍】
    jvm堆内存划分(jdk1.8以前):
    深入研究java gc

    jvm堆内存划分(jdk1.8之后):
    深入研究java gc

    java堆内存模型
    java的垃圾收集主要指的是java堆内存空间,那么在每一次执行GC的时候需要区分出那些堆内存空间需要被回收,那些不应该被回收。 所以为了整个的回收处理方便,JVM将堆内存分为如下的几个组成部分。而这几个组成部分你还需要去考虑JDK的版本,现在的JVM内存划分就必须考虑JDK1.8以前和JDK1.8之后的问题了。
    如果简化点来理解的话:
    1、新生代:那些刚刚创建的对象,刚刚创建的对象有可能会存在有许多垃圾对象,那么这些对象应该是被优先回收的;
    2、老年代:老不死的那类对象,经过了很多次的清理之后你发现该对象依然有用,
    3、永久代:intern()方法进行入池的对象实际上就在永久代中,永久代不会被回收。因为其本身属于一个bug性的存在(也就是jdk崩溃了,死了永久代CIA能消失),所以在jdk1.8之后,将其更换为元空间(就是电脑的直接内存)。
    举个例子:我电脑有100G内存,80G给了堆内存,那剩下的20G就可以给元空间。

    在整个内存的组成过程之中,每一代的内存空间都会有一个伸缩区,那么该区域就可以由JVM根据空间的使用情况,动态扩充。
    当我们适当合理的设置了伸缩区的内存大小之后,那么就可以得到良好的性能提升。也就是说最容易的性能提升就是改变伸缩区的内存大小。

    首先什么是java gc 、java对象创建流程
    java对象创建流程如图:
    深入研究java gc

    1、大多数内存对象要么生存周期比较短,很快就会没人引用,比如处理RPC请求的buffer可能只会生存几微秒;
    2、要么生存周期比较长,比如Block Cache中的热点Block,可能就会生存几分钟,甚至更长时间。
    3、基于这样的事实,JVM将整个堆内存分为两个部分:新生代(young generation)和老生代(tenured generation),除此之外,JVM还有一个非堆内存区-Perm区,主要存放class信息以及其他meta元信息,
    4、其中Young区又分为Eden区和两个Survivor 区:S0和S1。
    5、一个内存对象在创建之后,首先会为其在新生代申请一块内存空间,如果这个对象在新生代存活了很长时间,会将其迁移到老生代。 
    6、在大多数对延迟敏感的业务场景下(比如HBase),建议使用如下JVM参数,-XX:+UseParNewGC和XX:+UseConcMarkSweepGC,其中前者表示对新生代执行并行的垃圾回收机制,而后者表示对老生代执行并行标记-清除垃圾回收机制。
    7、可见,JVM允许针对不同内存区执行不同的GC策略。
    //在 cdh中默认是这样设置的
    -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled


    接下来重点先讨论一下年轻代

    深入研究java gc

    年轻代GC实现复制算法:(年轻代GC策略 – Parallel New Collector)
    深入研究java gc

    1、对象初始化之后会被放入Young区,更具体的话应该是Eden区,当Eden区满了之后,会进行一次GC。
    2、GC算法会检查所有对象的引用情况,如果某个对象还有被引用,表示该对象存活。
    3、检查完成之后,会将这些存活的对象移到S0区,并且回收整个Eden区空间,称为一次Minor GC;
    4、接着新对象进来,又会放入Eden区,满了之后会检查S0和Eden区存活的对象,将所有存活的对象移到S1区,再回收整个S0和Eden区空间;
    5、很容易理解,S0和S1两个区总会有一个区是预留给下次存放存活对象用的。

    这种算法称为复制算法,对于这种算法,有两点需要关注:

    1. 算法会执行’stop-the-world’暂停,但时间非常短。因为Young区通常会设置的比较小(一般不建议不超过512M),而且JVM会启动大量线程并发执行,一次Minor GC一般都会在几毫秒内完成
    2. 不会产生碎片,每次GC之后都会将存活的对象放入连续的空间(S0或S1)
      内存中所有对象都会维护一个计数器,每次Minor GC移动一个对象之后,都会为这个对象的计数器加一。当计数器增加到一定阈值之后,算法就会认为该对象生命周期很长,会将其移入老生代。该阈值可以通过JVM参数XX:MaxTenuringThreshold指定。

    提高了解篇
    年轻代优化算法

    深入研究java gc

    深入研究java gc

    深入研究java gc

    年轻代内存调整参数(重要):
    深入研究java gc


    接下来深度研究老年代

    什么是老年代 和老年代的full gc:
    深入研究java gc

    老生代GC策略 – Concurrent Mark-Sweep(CMS算法)
    什么是CMS 为什么CMS?
    1、每次执行Minor GC之后,都会有部分生命周期较长的对象被移入老生代,一段时间之后,老生代空间也会被占满。
    2、此时就需要针对老生代空间执行GC操作,此处我们介绍Concurrent Mark-Sweep(CMS)算法。

    CMS算法整个流程分为6个阶段,其中部分阶段会执行 ‘stop-the-world’ 暂停,部分阶段会和应用线程一起并发执行:
    如图:
    深入研究java gc

    老年代执行CMS过程:
    深入研究java gc
    相应的,对于CMS算法,也需要关注两点:

    1. ‘stop-the-world’暂停时间也很短暂,耗时较长的标记和清理都是并发执行的。
    2. CMS算法在标记清理之后并没有重新压缩分配存活对象,因此整个老生代会产生很多的内存碎片。

    提高篇 
    老年代标记清除算法:
    深入研究java gc

    老年代标记压缩算法:
    深入研究java gc

    老年代内存调整参数:
    深入研究java gc

    永久代调整参数:
    深入研究java gc

    元空间调整参数:
    深入研究java gc


    可用gc方式小结:
    深入研究java gc

    年轻代串行GC(copy)
    深入研究java gc

    年轻代并行回收GC
    深入研究java gc

    年轻代并行GC
    深入研究java gc

    老年代串行GC
    深入研究java gc

    老年代并行GC
    深入研究java gc

    常用gc策略:
    深入研究java gc

    GC调整策略、
    深入研究java gc

    收集器参数设置
    深入研究java gc


    G1收集器介绍:
    深入研究java gc

    深入研究java gc

    深入研究java gc

    深入研究java gc

    深入研究java gc

    深入研究java gc

    参考链接:
    http://hbasefly.com/2016/05/21/hbase-gc-1/

  • 相关阅读:
    GridView 几个受保护的方法的注释
    完全理解 IDisposable 接口的实现
    C++ 函数调用约定和名称修饰
    硬盘格式转换不影响数据_ convert命令FAT32转NTFS
    Win7系统修复_修复光盘的制作与使用
    双网卡共享上网设置
    Windows?XP系统修复方法
    无线路由器与有线路由器的连接(两个路由器连接)
    无线路由器“无线漫游”
    登录路由器没有弹出登录框_路由器无法登录解决办法
  • 原文地址:https://www.cnblogs.com/zgzf/p/11028047.html
Copyright © 2011-2022 走看看