zoukankan      html  css  js  c++  java
  • JVM 1.8 调优小结

    JVM GC 模型

    .JVM 调优参考

    .  JVM 收集器选择

    一篇比较好的参考:https://lijie.blog.csdn.net/article/details/105299403

    JVM GC 模型

    GC:

    复制代码
    新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。
    新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace) ,所有的类都是在伊甸区被new出来的。
    幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。
    当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。
    若幸存 0区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再移动到养老区。
    若养老区也满了,那么这个时候将产生Major GC(FullGC)进行养老区的内存清理。

    若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。 如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二: (1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。 (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。 ----内存溢出;内存泄漏
    复制代码

     .JVM 调优参考

    推荐阅读: 参考: https://www.cnblogs.com/jpfss/p/9753215.html

    总结: 百万连接,百亿吞吐量服务的JVM性能调优实战   >>https://my.oschina.net/LucasZhu/blog/2056232 

    最后,对于长连接,push一类的海量服务端应用,16G内存8核心,推荐的JVM参数如下 jdk 1.7 14g->13g

    -Xms13g -Xmx13g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4
    -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly
    -XX:CMSInitiatingOccupancyFraction=70 -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled
    -XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime
    -XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError
    

    JDK1.8

    -Xms13g -Xmx13g -Xss512k -XX:MetaspaceSize=384m -XX:MaxMetaspaceSize=384m -XX:NewSize=11g -XX:MaxNewSize=11g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC
    -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC 
    -XX:+CMSScavengeBeforeRemark -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0
    -XX:-ReduceInitialCardMarks -XX:+CMSClassUnloadingEnabled -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime 
    -XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError
    

    这样可以保证大多数对象在new区域就销毁,并且到了old区,remark之前先yong gc,然后再来一次cms old gc,将old gc控制在毫秒级别

    执行启动设置Jvm参数的操作。

    java -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC myApp-1.0.0.jar

    关于这些设置的JVM参数是什么意思,请参考第二步中的oracle官方给出的调优文档。

    我在这边简单说一下: .JVM 调优参考

    -XX:MetaspaceSize=128m (元空间默认大小) 
    -XX:MaxMetaspaceSize=128m (元空间最大大小) 
    -Xms1024m (堆最大大小) 
    -Xmx1024m (堆默认大小) 
    -Xmn256m (新生代大小) 
    -Xss256k (棧最大深度大小) 
    -XX:SurvivorRatio=8 (新生代分区比例 8:2)  配置新生代中 eden from to 比例关系 8 1 1
    -XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器) 
    -XX:+PrintGCDetails (打印详细的GC日志)

    1. -XX:SurvivorRatio=2这个参数配置什么意思 
    2. * SurvivorRatio=2  配置新生代中 eden from to 比例关系 2 1 1
     

    知识点:

    JDK8之后把-XX:PermSize 和 -XX:MaxPermGen移除了,取而代之的是 
    -XX:MetaspaceSize=128m (元空间默认大小) 
    -XX:MaxMetaspaceSize=128m (元空间最大大小) 
    JDK 8开始把类的元数据放到本地化的堆内存(native heap)中,这一块区域就叫Metaspace,中文名叫元空间。 
    使用本地化的内存有什么好处呢?最直接的表现就是java.lang.OutOfMemoryError: PermGen 空间问题将不复存在,因为默认的类的元数据分配只受本地内存大小的限制,也就是说本地内存剩余多少,理论上Metaspace就可以有多大(貌似容量还与操作系统的虚拟内存有关?这里不太清楚),这解决了空间不足的问题。

    不过,让Metaspace变得无限大显然是不现实的,因此我们也要限制Metaspace的大小:使用-XX:MaxMetaspaceSize参数来指定Metaspace区域的大小。JVM默认在运行时根据需要动态地设置MaxMetaspaceSize的大小。

    复制代码
    GC模式
    G1中提供了三种模式垃圾回收模式,young gc、mixed gc 和 full gc,在不同的条件下被触发。
    
    young gc
    发生在年轻代的GC算法,一般对象(除了巨型对象)都是在eden region中分配内存,当所有eden region被耗尽无法申请内存时,就会触发一次young gc,这种触发机制和之前的young gc差不多,
    执行完一次young gc,活跃对象会被拷贝到survivor region或者晋升到old region中,空闲的region会被放入空闲列表中,等待下次被使用。 参数 含义 -XX:MaxGCPauseMillis 设置G1收集过程目标时间,默认值200ms -XX:G1NewSizePercent 新生代最小值,默认值5% -XX:G1MaxNewSizePercent 新生代最大值,默认值60% mixed gc 当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制。 那么mixed gc什么时候被触发? 先回顾一下cms的触发机制,如果添加了以下参数: -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly 当老年代的使用率达到80%时,就会触发一次cms gc。相对的,mixed gc中也有一个阈值参数 -XX:InitiatingHeapOccupancyPercent,当老年代大小占整个堆大小百分比达到该阈值时,会触发一次mixed gc. mixed gc的执行过程有点类似cms,主要分为以下几个步骤: initial mark: 初始标记过程,整个过程STW,标记了从GC Root可达的对象 concurrent marking: 并发标记过程,整个过程gc collector线程与应用线程可以并行执行,标记出GC Root可达对象衍生出去的存活对象,并收集各个Region的存活对象信息 remark: 最终标记过程,整个过程STW,标记出那些在并发标记过程中遗漏的,或者内部引用发生变化的对象 clean up: 垃圾清除过程,如果发现一个Region中没有存活对象,则把该Region加入到空闲列表中 full gc 如果对象内存分配速度过快,mixed gc来不及回收,导致老年代被填满,就会触发一次full gc,G1的full gc算法就是单线程执行的serial old gc,会导致异常长时间的暂停时间,需要进行不断的调优,尽可能的避免full gc. 链接:https://www.jianshu.com/p/0f1f5adffdc1
    复制代码

     .  JVM 收集器选择

        垃圾回收器的选择 -> 原文链接:https://blog.csdn.net/zhaodongchao1992/article/details/106566984/

    除非您的应用程序有非常严格的暂停时间要求,否则请先运行您的应用程序并允许VM选择收集器。如有必要,请调整堆大小以提高性能。如果性能仍然不能达到您的目标,请使用以下准则作为选择收集器的起点。

    1 如果应用程序的数据集较小(最大约100 MB),则选择带有选项-XX:+ UseSerialGC的串行收集器。

    2 如果应用程序将在单个处理器上运行,并且没有暂停时间要求,则让VM选择收集器,或通过选项-XX:+ UseSerialGC选择串行收集器。

    3 如果(a)峰值应用程序性能是第一要务,并且(b)没有暂停时间要求或可接受1秒或更长时间的暂停,则让VM选择收集器,或使用-XX:+ UseParallelGC选择并行收集器。

    4 如果响应时间比整体吞吐量更重要,并且垃圾收集暂停时间必须保持小于1秒,那么请使用-XX:+UseConcMarkSweepGC或-XX:+ UseG1GC选择并发收集器。



    这些准则仅为选择收集器提供了一个起点,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处理器的数量和速度。暂停时间对这些因素特别敏感,因此前面提到的1秒阈值仅是近似值:在许多数据大小和硬件组合上,并行收集器的暂停时间将超过1秒。相反,在某些组合上,并发收集器可能无法将暂停时间保持在1秒以内

    我比较看着响应时间,因此这里选择使用标记清除算法的CMS垃圾回收器,使用配置:-XX:+UseConcMarkSweepGC

        -XX:+PrintFlagsFinal 可以打印jvm的详细配置信息
        -XX:+PrintTenuringDistribution 可以打印新生代到老年代的阀值和对象的寿命。

    5 元数据空间大小的分配

    在Java8中,移除了之前的永久代,将类的元数据信息存放在一块非堆的本地内存中,这一块区域被称之为元数据区(Metaspace),不设置的话元数据空间的大小有机器内存的大小来决定。在实际情况最好是设置一个合适的值。

    做如下配置:
    -XX:MetaspaceSize=128m
    -XX:MaxMetaspaceSize=256m

    直接内存大小的分配
    直接内存顾名思义就是机器上的一块内存。也是一块非堆的内存。直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

    这篇文章对直接内存有详细说明:https://blog.csdn.net/y3over/article/details/88791958

    jvm设置参数如下(直接内存默认大小为64m):
    -XX:MaxDirectMemorySize=1024m
    -XX:+DisableExplicitGC

        -XX:+DisableExplicitGC 禁止在代码中显示调用System.gc() 。因为在使用直接内存时会使用DirectByteBuffer对象。它在分配直接内存时会经常调用System.gc(),为了减少gc此时。线上环境建议禁止显示调用System.gc()

    综上得到如下配置:


    -XX:MaxDirectMemorySize=1024m //一块非堆的内存

    -XX:MetaspaceSize=128m (元空间默认大小) 
    -XX:MaxMetaspaceSize=128m (元空间最大大小) 
    -Xms1024m (堆最大大小) 
    -Xmx1024m (堆默认大小) 
    -Xmn256m (新生代大小) 
    -Xss256k (设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。)
    -XX:SurvivorRatio=8 (新生代分区比例 8:2)  配置新生代中 eden from to 比例关系 8 1 1
    -XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)    或者: -XX:+UseG1GC
    -XX:+PrintGCDetails (打印详细的GC日志)
    -Xloggc:/var/app/loggs/gc.log
    -XX:+UseConcMarkSweepGC
    -XX:+PrintTenuringDistribution
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/var/app/log/dump/java_heapdump.hprof
    -XX:+DisableExplicitGC     //禁止在代码中显示调用System.gc()
    -XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。

    -XX:ParallelGCThreads=20:配置并行收集器的线程数,
    -XX:+UseAdaptiveSizePolicy
    :设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

     CMS:
    CMS 收集器:老年代。是一种以获取最短回收停顿时间为目标的收集器,适用于互联网站或者 B/S 系统的服务端上。
    特点:

    针对老年代,采用标记-清楚法清除垃圾;
    基于"标记-清除"算法(不进行压缩操作,产生内存碎片);
    以获取最短回收停顿时间为目标;
    并发收集、低停顿;
    CMS收集器有3个明显的缺点:1.对CPU资源非常敏感、2.无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败、3.产生大量内存碎片
    垃圾收集线程与用户线程(基本上)可以同时工作
     

    使用方式: 设置垃圾收集器:"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;

    G1
    G1 收集器:分代收集器。当今收集器技术发展最前沿成果之一,是一款面向服务端应用的垃圾收集器。

    G1可以说是CMS的终极改进版,解决了CMS内存碎片、更多的内存空间登问题。虽然流程与CMS比较相似,但底层的原理已是完全不同。
    特点:

    能充分利用多CPU、多核环境下的硬件优势;
    可以并行来缩短(Stop The World)停顿时间;
    也可以并发让垃圾收集与用户程序同时进行;
    分代收集,收集范围包括新生代和老年代
    能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;
    能够采用不同方式处理不同时期的对象;
    应用场景可以面向服务端应用,针对具有大内存、多处理器的机器;
    采用标记-整理 + 复制算法来回收垃圾

    使用方式:
    //如何设置JVM参数底下会讲解:这里只是列举一部分参数:
    
    设置垃圾收集器:"-XX:+UseG1GC":指定使用G1收集器;
    设置垃圾收集器参数:"-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;
    设置垃圾收集器参数:"-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;
    设置垃圾收集器参数:"-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region
    

     JVM内存参数简述

    复制代码
    #常用的设置
    -Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。 
    
    -Xmx:最大堆大小,JVM 运行过程中,如果初始堆空间不足的时候,最大可以扩展到多少。 
    
    -Xmn:设置堆中年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。 
    
    -XX:NewSize=n 设置年轻代初始化大小大小 
    
    -XX:MaxNewSize=n 设置年轻代最大值
    
    -XX:NewRatio=n 设置年轻代和年老代的比值。如: -XX:NewRatio=3,表示年轻代与年老代比值为 1:3,年轻代占整个年轻代+年老代和的 1/4 
    
    -XX:SurvivorRatio=n 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。8表示两个Survivor :eden=2:8 ,即一个Survivor占年轻代的1/10,默认就为8
    
    -Xss:设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。
    
    -XX:ThreadStackSize=n 线程堆栈大小
    
    -XX:PermSize=n 设置持久代初始值    
    
    -XX:MaxPermSize=n 设置持久代大小
     
    -XX:MaxTenuringThreshold=n 设置年轻带垃圾对象最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。
    
    #下面是一些不常用的
    
    -XX:LargePageSizeInBytes=n 设置堆内存的内存页大小
    
    -XX:+UseFastAccessorMethods 优化原始类型的getter方法性能
    
    -XX:+DisableExplicitGC 禁止在运行期显式地调用System.gc(),默认启用    
    
    -XX:+AggressiveOpts 是否启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等,jdk6纸之后默认启动
    
    -XX:+UseBiasedLocking 是否启用偏向锁,JDK6默认启用    
    
    -Xnoclassgc 是否禁用垃圾回收
    
    -XX:+UseThreadPriorities 使用本地线程的优先级,默认启用    
    
    复制代码

    JVM的GC收集器设置:

    复制代码
     -XX:+UseSerialGC:设置串行收集器,年轻带收集器 
    
     -XX:+UseParNewGC:设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值。
     
     -XX:+UseParallelGC:设置并行收集器,目标是目标是达到可控制的吞吐量
    
     -XX:+UseParallelOldGC:设置并行年老代收集器,JDK6.0 支持对年老代并行收集。 
    
     -XX:+UseConcMarkSweepGC:设置年老代并发收集器
    
     -XX:+UseG1GC:设置 G1 收集器,JDK1.9默认垃圾收集器
  • 相关阅读:
    python基础——反射
    python基础——模块
    python基础——异常处理、递归
    python基础——生成器与迭代器
    python基础——装饰器
    Python常见面试题
    Socket网络编程
    爬虫解析相关
    爬虫请求相关
    初识爬虫
  • 原文地址:https://www.cnblogs.com/telwanggs/p/15122860.html
Copyright © 2011-2022 走看看