zoukankan      html  css  js  c++  java
  • JAVA GC优化入门

    为什么需要优化GC?

      JAVA的GC是面试必考的题目,可是在实际项目中什么时候使用GC哪?或者应该什么时候优化GC哪?有句名言:“GC优化永远是最后一项任务”。

      在使用GC之前,应该考虑一下进行GC的最根本原因:垃圾收集器需要清除在程序中创建的对象,GC执行的次数即需要被垃圾收集器清理的对象个数,与创建对象的数量成正比,因此,首先应该减少创建对象的数量,我们应该从小事做起,比如需要使用StringBuilder 或者StringBuffer 来替代String;应该尽量少的输出日志;

      但是,我们知道有些情况会让我们束手无策,我们眼睁睁的看着XML以及JSON解析占用了大量的内存。即便我们已经尽可能少的使用String以及尽量少的输出日志,但是大量的临时内存仍然被用于XML或者JSON解析,例如10-100MB。但是,舍弃XML和JSON是很难的;或者程序偶尔内存溢出;或者假死影响到了正常的服务,这个时候就可以考虑优化GC了。

      那么优化GC可以首相到两个方面:1、一个是将转移到老年代的对象数量降到最少;2、另一个是减少Full GC的执行时间。

    将转移到年老代的对象数量降到最少

      分代垃圾回收策略是由Oracle JVM提供,不包括可以在JDK7以及更高版本中使用的G1 GC。换句话说,对象被创建在伊甸园空间,而后转化到幸存者空间,最终剩余的对象被送到老年代。某些比较大的对象会在被创建在伊甸园空间后,直接转移到年老代空间。年老代空间上的GC处理会年轻代花费更多的时间。因此,减少被移到年老代对象的数据可以显著地减少Full GC的频率。减少被移到年老代空间的对象的数量,可能会被误解为将对象留在新生代。但是,这是不可能的。取而代之,可以调整新生代空间的大小

    减少Full GC执行时间

      Full GC的执行时间比Minor GC要长很多。因此,如果Full GC花费了太多的时间(超过1秒),一些连接的部分可能会发生超时错误。这个时候如果你单纯试图通过减少年老代空间来减少Full GC的执行时间,可能会导致OutOfMemoryError 或者 Full GC执行的次数会增加。与之相反,如果你试图通过增加老年代空间来减少Full GC执行次数,执行时间又会增加。因此,你需要将老年代空间设定为一个“合适”的值。

    影响GC性能的参数

     不要幻想“某个人设定了GC参数后性能得到极大的提高,我们为什么不和他用一样的参数?”,因为不同的Web服务所创建对象的大小和他们的生命周期都不尽相同。

      Java GC参数设定一些参数不但没有提高GC执行速度,反而可能导致他更慢。GC优化的最基本原则是将不同的GC参数用于2台或者多台服务器,并进行对比,并将那些被证明提高了性能或者减少了GC执行时间的参数应用于服务器。下面这个表格列出了GC参数中与内存大小相关的,可以影响性能的参数:

                     表1:GC优化需要考虑的Java参数

    定义 参数 说明
    堆内存 -Xms 启动JVM时的堆内存空间大小
      -Xmx 堆内存的最大值
    年轻代 -XX:NewRatio 年轻代与年老代的比值
      -XX:NewSize 年轻代大小
      -XX:SurvivorRatio 伊甸园空间和幸存者空间的比值

      我在进行GC优化时经常使用-Xms,-Xmx和-XX:NewRatio。另一个可能影响GC性能的参数是GC类型。下表列出了所有可选的GC类型(基于JDK6.0)。

    分类 参数 备注
    Serial GC -XX:+UseSerialGC  
    Parallel GC -XX:+UseParallelGC  
    -XX:ParallelGCThreads=value
    Parallel Compacting GC -XX:+UseParallelOldGC  
    CMS GC -XX:+UseConcMarkSweepGC  
    -XX:+UseParNewGC
    -XX:+CMSParallelRemarkEnabled
    -XX:CMSInitiatingOccupancyFraction=value
    -XX:+UseCMSInitiatingOccupancyOnly
    G1 -XX:+UnlockExperimentalVMOptions 在JDK6中这两个参数必须同时使用
    -XX:+UseG1GC

    GC优化过程

    GC优化的过程与大多数性能改善的过程及其类似。下面是使用的GC优化过程:

      1.监控GC状态

        首先你需要监控GC来检查在系统执行过程中GC的各种状态。

      2.在分析监控结果后,决定是否进行GC优化

        在检查GC状态的过程中,你应该分析监控结果以便决定是否进行GC优化,如果分析结果表明执行GC的时间只有0.1-0.3秒,那你就没必要浪费时间去进行GC优化。但是,如果GC的执行时间是1-3秒,或者超过10秒,GC将势在必行。

      3. 调整GC类型/内存空间

        如果你已经决定要进行GC优化,那么就要选择GC类型和设定内存空间。在这时,如果你有几台不同服务器,请时刻牢记,检查每一台服务器的GC参数,并进行有针对性的优化。

      4.分析结果

        在调整了GC参数并持续收集24小时之后,开始对结果进行分析,如果你幸运的话,你就找到那些最适合系统的GC参数。反之,你需要通过分析日志来检查内存是如何被分配的。然后你需要通过不断的调整GC类型和内存空间大小一边找到最佳的参数。

      5. 如果结果令人满意,你可以将该参数应用于所有的服务器,并停止GC优化

        有过GC优化结果令人满意,你可以应用于所有的服务器,下面,我们将看到每个步骤的具体任务。

    监控GC状态及分析结果

      查看运行中的Web Application Server (WAS)的GC状态的最佳方法是通过jstat命令,下面这个例子展现了某个JVM在进行GC优化之前的状态。

      如上表,我们先看一下YGC 和YGCT,计算YGCT/YGC,172.623/3428=0.050秒(50毫秒)。这意味着新生代空间上的GC操作平均花费50毫秒。在这种情况,你大可不必担心新生代空间上执行的GC操作。接下来,我们来看一下FGCT 和FGC。计算FGCT/ FGC得到19.68秒,这意味着GC的平均执行时间为19.68秒,可能是每次花费19.68秒执行了三次,也可能是其中的两次执行了1秒而另一次执行了58秒。不论哪种情况,都需要进行GC优化。

      通过jstat 命令可以很轻易地查看GC状态,但是,分析GC的最佳方式是通过–verbosegc参数来生成日志。如果GC执行时间满足下面所有的条件,就意味着无需进行GC优化了:

    • Minor GC执行的很快(小于50ms)
    • Minor GC执行的并不频繁(大概10秒一次)
    • Full GC执行的很快(小于1s)
    • Full GC执行的并不频繁(10分钟一次)

      上面提到的数字并不是绝对的;他们根据服务状态的不同而有所区别,某些服务可能满足于Full GC每次0.9秒的速度,但另一些可能不是。因此,针对不同的服务设定不同的值以决定是否进行GC优化。

      在查看GC状态的时候有件事你需要特别注意,那就是不要只关注Minor GC 和Full GC的执行时间。还要关注GC执行的次数,例如,当新生代空间较小时,Minor GC会过于频繁的执行(有时每秒超过1次)。另外,转移到老年代的对象数增多,则会导致Full GC执行次数增多。因此,别忘了加上–gccapacity参数来查看具体占用了多少空间。

    设定GC类型/内存空间大小

      1.设定GC类型

      OracleJVM有5种GC类型,但是在JDK7之前的版本中,只能在Parallel GC, Parallel Compacting GC 和CMS GC之中选择一个,对于选择哪个没有明确的原则和规则,但是有一点是很明确的:CMS GC比Parallel GCs更快。

      但是,CMS GC也不总是更快。整体来看,CMS GC模式下的Full GC执行更快,不过,一旦出现并行模式失败,他将比Parallel GC更慢。

      CONCURRENT MODE失败

      在说明这个问题之前,首先要明白Parallel GC 和 CMS GC 最大的不同来自于压缩任务。压缩任务是通过删除已分配内存空间中的空白空间以便压缩内存,清理内存碎片。在Parallel GC模式下,压缩工作在Full GC执行时进行,这会费很多时间,但是,在执行完Full GC之后,由于能够顺序地分配空间,随后的内存能够被更快的分配。

      与之相反的,CMS GC并不进行压缩处理,因此,CMS GC执行的更快。但是,由于没有压缩,在进行磁盘清理之前,内存中会有很多空白空间。这就是说,可能没有足够的空间存储大的对象,例如,虽然老年代空间还有300MB空间,但是一些10MB的对象无法被顺序的存储。在这种情况下,会出现“并行模式失败”警告,并执行压缩处理。在CMS GC模式下,压缩处理的执行时间要比Parallel GCs长很多。这样CMS还不许Parallel GCs效率高。综上所述,你需要找到最适合你的系统的GC类型。每个系统都有最适合他的GC类型等着你去寻找,如果你有6台服务器。我建议你每两台设置相同的参数。并添加 –verbosegc参数,分析结果。

      2.设定内存空间大小

      下面特别精辟的展示了内存空间大小、GC执行次数和GC执行时间三者间的关系:

    • 大内存空间
      • 减小GC执行次数
      • 增加GC执行时间
    • 小内存空间
      • 减小GC执行时间
      • 增加GC执行次数

      关于如何设置内存空间的大小,没有唯一的标准答案。如果服务器资源足够,而且Full GC也可能在1秒内完成,设置为10GB当然可行。但绝大多数服务器并不是这样,当内存设为10GB时,可能要花费10~30秒来执行Full GC。当然,执行时间会随对象的大小而改变。

      鉴于如此,我们应该如何设定内存空间大小呢?一般来说,我建议为500MB。不过请注意这不是让你将WAS的内存参数设置为–Xms500m 和–Xmx500m。根据优化GC之前的状态,如果Full GC执行之后内存空间剩余300MB,那么最好将内存设置为1GB(300MB(默认程序占用)+ 500MB(老年代最小空间)+200MB(空闲内存))。也就是说你要为老年代额外设置500MB。因此,如果你有三个执行服务器,内存分别设置为1GB,1.5GB,2GB,并且检查结果。 

      理论上来讲,GC执行速度应该遵循1GB> 1.5GB> 2GB,因此1GB执行GC速度最快。但是并不说明1GB空间的Full GC会花费1秒而2GB空间会花费2秒。时间取决于服务器的性能和对象的大小。因此,最佳的方式是建立尽可能多的衡量指标来监控他们。

      对于内存空间大小,你应该额外设定NewRatio参数。NewRatio参数是新生代和老年代空间的比例,即XX:NewRatio=1意味着新生代与老年代之比为1:1。对于1GB来说就是新生代和老年代各500MB。如果NewRatio为2,意味着新生代老年代之比为1:2,因此该值越大,老年代空间越大,新生代空间越小。

      这看似一件不是很重要的事情,但NewRatio参数会显著地影响整个GC的性能。如果新生代空间很小,会用更多的对象被转移到老年代空间,这样导致频繁的Full GC,增加暂停时间。你可以简单的认为NewRatio 为1是最佳的选择,但是,有时可能设置为2或3更好,我就见过很多这样的例子。

       如何最快的完成GC优化?对比性能测试的结果应该是最快地方法,为每一台服务器设置不同的参数并监控他们的状态,强烈建议至少监控1或2天的数据。但是,当你对GC优化是,你要确保每次执行相同的负载。并且请求的比率,例如URL都应该是一致的。不过,即便对于专业测试人员要想精确的控制负载也是很难的,并要花费大量的时间准备。因此,相对来说比较方便和容易的方法是调整才参数,之后花费较长的时间收集结果。

  • 相关阅读:
    什么是MIME
    bit/byte/英文字符/汉字之间的换算及java八大基本数据类型的占字节数
    js 上传文件大小检查
    java.toString() ,(String),String.valueOf的区别
    java 下载文件的样例
    回调函数分析
    IO流详析
    各个秒之间的换算率
    内边距:
    Less-6【报错+BOOL类型】
  • 原文地址:https://www.cnblogs.com/liqiu/p/3663905.html
Copyright © 2011-2022 走看看