加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

JVM调优

发布时间:2020-12-15 07:24:31 所属栏目:Java 来源:网络整理
导读:CMS收集器 CMS收集器是基于“标记—清除”算法实现,整个过程分为4个步骤,包括: 初始标记(CMS initial mark) 并发标记(CMS concurrent mark) 重新标记(CMS remark) 并发清除(CMS concurrent sweep) 其中,初始标记、重新标记这两个步骤仍然需要“S

CMS收集器

CMS收集器是基于“标记—清除”算法实现,整个过程分为4个步骤,包括:
    初始标记(CMS initial mark)
    并发标记(CMS concurrent mark)
    重新标记(CMS remark)
    并发清除(CMS concurrent sweep)

    其中,初始标记、重新标记这两个步骤仍然需要“Stop The World”。

    初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快

    并发标记阶段就是进行GC Roots Tracing的过程

    重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,
    这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。


CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。
    由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,
    这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。
    这一部分垃圾就称为“浮动垃圾”。

也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,
因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。

1.6中,CMS收集器的启动阈值已经提升至92%。
要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:
    临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
    所以说参数-XX:CMSInitiatingOccupancyFraction(超过此值激活CMS收集器)
    设置得太高很容易导致大量“Concurrent Mode Failure”失败,性能反而降低。


CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数(默认就是开启的),
用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,
空间碎片问题没有了,但停顿时间不得不变长。


虚拟机设计者还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,
这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)。

G1收集器

G1是一款面向服务端应用的垃圾收集器。
HotSpot开发团队赋予它的使命是未来可以替换掉JDK 1.5中发布的CMS收集器。

并行与并发:
    G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,
    部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。

分代收集:
    与其他收集器一样,分代概念在G1中依然得以保留。
    虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,
    但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。

空间整合:
    与CMS的“标记—清理”算法不同,G1从整体来看是基于“标记—整理”算法实现的收集器,
    从局部(两个Region之间)上来看是基于“复制”算法实现的,
    但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。
    这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。

可预测的停顿:
    这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,
    但G1除了追求低停顿外,还能建立可预测的停顿时间模型,
    能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,。

在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不再是这样。
使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),
虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。

触发full gc的情况

(1)System.gc()方法的调用。
    此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。
    强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,
    可通过通过 -XX:+DisableExplicitGC 来禁止RMI(Java远程方法调用)调用System.gc。

(2)年老代空间不足。
    年老代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,
    当执行Full GC后空间仍然不足,则抛出错误:java.lang.OutOfMemoryError: Java heap space。
    为避免以上两种状况引起的Full GC,
    调优时应尽量做到让对象在Minor GC(新生代GC)阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

(3)Permanet Generation空间满了。
    Permanet Generation中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,
    Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。
    如果经过Full GC仍然回收不了,那么JVM会抛出错误信息:java.lang.OutOfMemoryError: PermGen space 。
    为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

(4)通过Minor GC(新生代GC)后进入老年代的平均大小大于老年代的可用内存。
    如果发现统计数据说之前Minor GC的平均晋升大小比目前old gen剩余的空间大,
    则不会触发Minor GC(新生代GC)而是转为触发full GC。

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,
    则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

内存泄漏

年老代堆空间被占满
    异常: java.lang.OutOfMemoryError: Java heap space

解决方案:
    代码内的内存泄漏可以通过一些分析工具进行分析,然后找出泄漏点进行改善。
    Survivor Space区域内存不够导致大量对象进如年老代,通过优化代码和增加Survivor Space等方式去优化。


持久代被占满(大量动态反射生成的类不断被加载)
    异常:java.lang.OutOfMemoryError: PermGen space

解决方案:
    增加持久代的空间 -XX:MaxPermSize=100M。
    如果有自定义类加载的需要排查下自己的代码问题。


堆栈溢出
    异常:java.lang.StackOverflowError
    一般就是递归没返回,或者循环调用造成


线程堆栈满
    异常:Fatal: Stack size too small
    java中一个线程的空间大小是有限制的。JDK5.0以后这个值是1M。与这个线程相关的数据将会保存在其中。
    但是当线程空间满了以后,将会出现上面异常。

解决:
    增加线程栈大小。-Xss2m。但这个配置无法解决根本问题,还要看代码部分是否有造成泄漏的部分。


系统内存被占满
    异常:java.lang.OutOfMemoryError: unable to create new native thread
    这个异常是由于操作系统没有足够的资源来产生这个线程造成的。
    系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。

解决:
    1. 重新设计系统减少线程数量。
    2. 线程数量不能减少的情况下,通过-Xss减小单个线程大小。以便能生产更多的线程。

优化GC步骤

首先需要观察目前垃圾回收的情况,分析出老年代和年轻代回收的情况,适当的去调整内存大小和-XX:SurvivorRatio的比例。

根据垃圾收集器的特性,选择适合自己业务的垃圾收集器,一般来说现在的WEB服务都是CMS+ParNew收集器。
根据CMS收集器一般来说就会产生大量碎片,根据自己的需求悬着相应的压缩频率即可。

不断的调整jvm内存比例,老年代、年轻代、以及持久代的比例,直到测试出一个比较满意的值。

JVM参数配置

https://www.cnblogs.com/loveer/p/11522493.html

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读