zoukankan      html  css  js  c++  java
  • web服务之Tomcat性能优化JVM

    Tomcat 性能优化

    在目前流行的互联网架构中,Tomcat在目前的网络编程中是举足轻重的,由于Tomcat的运行依赖于
    JVM,从虚拟机的角度把Tomcat的调整分为外部环境调优 JVM 和 Tomcat 自身调优两部分

    JVM组成

    JVM 组成部分
    类加载子系统: 使用Java语言编写.java Source Code文件,通过javac编译成.class Byte Code文
    件。class loader类加载器将所需所有类加载到内存,必要时将类实例化成实例
    运行时数据区: 最消耗内存的空间,需要优化
    执行引擎: 包括JIT (JustInTimeCompiler)即时编译器, GC垃圾回收器
    本地方法接口: 将本地方法栈通过JNI(Java Native Interface)调用Native Method Libraries, 比
    如:C,C++库等,扩展Java功能,融合不同的编程语言为Java所用
    

    JVM运行时数据区域由下面部分构成:

    Method Area (线程共享):方法区是所有线程共享的内存空间,存放已加载的类信息(构造方法,接口定义),常量(final),静态变量(static), 运行时常量池等。但实例变量存放在堆内存中. 从JDK8开始此
    空间由永久代改名为元空间
    
    heap (线程共享):堆在虚拟机启动时创建,存放创建的所有对象信息。如果对象无法申请到可用内存将抛出OOM异常.堆是靠GC垃圾回收器管理的,通过-Xmx -Xms 指定最大堆和最小堆空间大小
    
    Java stack (线程私有):Java栈是每个线程会分配一个栈,存放java中8大基本数据类型,对象引用,实例的本地变量,方法参数和返回值等,基于FILO()(First In Last Out),每个方法为一个栈帧
    
    Program Counter Register(线程私有):PC寄存器就是一个指针,指向方法区中的方法字节码,每一个线程用于记录当前线程正在执行的字节码指令地址。由执行引擎读取下一条指令.因为线程需要
    切换,当一个线程被切换回来需要执行的时候,知道执行到哪里了
    
    Native Method stack(线程私有):本地方法栈为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等。
    
    所谓本地方法,使用native 关健字修饰的方法,比如:Thread.sleep方法. 简单的说是非Java实现的方
    法,例如操作系统的C编写的库提供的本地方法,Java调用这些本地方法接口执行。但是要注意,
    本地方法应该避免直接编程使用,因为Java可能跨平台使用,如果用了Windows API,换到了
    Linux平台部署就有了问题
    

    GC (Garbage Collection) 垃圾收集器

    在堆内存中如果创建的对象不再使用,仍占用着内存,此时即为垃圾.需要即使进行垃圾回收,从而释放内存
    空间给其它对象使用
    其实不同的开发语言都有垃圾回收问题,C,C++需要程序员人为回收,造成开发难度大,容易出错等问题,但
    执行效率高,而JAVA和Python中不需要程序员进行人为的回收垃圾,而由JVM或相关程序自动回收垃圾,减
    轻程序员的开发难度,但可能会造成执行效率低下
    堆内存里面经常创建、销毁对象,内存也是被使用、被释放。如果不妥善处理,一个使用频繁的进程,
    可能会出现虽然有足够的内存容量,但是无法分配出可用内存空间,因为没有连续成片的内存了,内存
    全是碎片化的空间。
    所以需要有合适的垃圾回收机制,确保正常释放不再使用的内存空间,还需要保证内存空间尽可能的保持一
    定的连续
    

    对于垃圾回收,需要解决三个问题

    哪些是垃圾要回收
    怎么回收垃圾
    什么时候回收垃圾
    

    Garbage 垃圾确定方法

    引用计数: 每一个堆内对象上都与一个私有引用计数器,记录着被引用的次数,引用计数清零,该
    对象所占用堆内存就可以被回收。循环引用的对象都无法将引用计数归零,就无法清除。Python中
    即使用此种方式
    
    根搜索(可达)算法 Root Searching
    

    垃圾回收基本算法

    标记-清除 Mark-Sweep

    分垃圾标记阶段和内存释放阶段。标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆,对未标记对象(即不再使用的对象)逐一进行清理。
    
    标记-清除最大的问题会造成内存碎片,但是不浪费空间,效率较高(如果对象较多,逐一删除效率也会影响)
    

    标记-压缩 (压实)Mark-Compact

    分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。
    
    标记-压缩算法好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。
    缺点是内存整理过程有消耗,效率相对低下
    

    复制 Copying

    先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如A。当A用完后,则将A中存活的对
    象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。
    缺点是比较浪费内存,只能使用原来一半内存,因为内存对半划分了,复制过程毕竟也是有代价。
    
    好处是没有碎片,复制过程中保证对象使用连续空间,且一次性清除所有垃圾,所以效率很高
    

    多种算法总结

    没有最好的算法,在不同场景选择最合适的算法

    效率: 复制算法>标记清除算法> 标记压缩算法
    内存整齐度: 复制算法=标记压缩算法> 标记清除算法
    内存利用率: 标记压缩算法=标记清除算法>复制算法
    

    STW

    对于大多数垃圾回收算法而言,GC线程工作时,停止所有工作的线程,称为Stop The World。GC 完成时,恢复其他工作线程运行。这也是JVM运行中最头疼的问题。
    

    分代堆内存GC策略

    上述垃圾回收算法都有优缺点,能不能对不同数据进行区分管理,不同分区对数据实施不同回收策略,分而治之。

    堆内存分代

    将heap内存空间分为三个不同类别: 年轻代、老年代、持久代

    Heap堆内存分为:
        年轻代Young:Young Generation
        伊甸园区eden: 只有一个,刚刚创建的对象
        幸存(存活)区Servivor Space:有2个幸存区,一个是from区,一个是to区。大小相等、地位
    相同、可互换。
            from 指的是本次复制数据的源区
            to 指的是本次复制数据的目标区
        老年代Tenured:Old Generation, 长时间存活的对象
        
        永久代:JDK1.7之前使用, 即Method Area方法区,保存JVM自身的类和方法,存储JAVA运行时的环境信息,JDK1.8后 改名为 MetaSpace,此空间不存在垃圾回收,关闭JVM会释放此区域内存,此空间物理上不属于heap内存,但逻辑上存在于heap内存
            永久代必须指定大小限制,字符串常量JDK1.7存放在永久代,1.8后存放在heap中
            MetaSpace 可以设置,也可不设置,无上限
    

    规律: 一般情况99%的对象都是临时对象

    年轻代回收 Minor GC

    1. 起始时,所有新建对象(特大对象直接进入老年代)都出生在eden,当eden满了,启动GC。这个称
      为Young GC 或者 Minor GC。
    2. 先标记eden存活对象,然后将存活对象复制到s0(假设本次是s0,也可以是s1,它们可以调
      换),eden剩余所有空间都清空。GC完成。
    3. 继续新建对象,当eden再次满了,启动GC。
    4. 先同时标记eden和s0中存活对象,然后将存活对象复制到s1。将eden和s0清空,此次GC完成
    5. 继续新建对象,当eden满了,启动GC。
    6. 先标记eden和s1中存活对象,然后将存活对象复制到s0。将eden和s1清空,此次GC完成

    以后就重复上面的步骤。
    通常场景下,大多数对象都不会存活很久,而且创建活动非常多,新生代就需要频繁垃圾回收。
    但是,如果一个对象一直存活,它最后就在from、to来回复制,如果from区中对象复制次数达到阈值
    (默认15次,CMS为6次,可通过java的选项 -XX:MaxTenuringThreshold=N 指定),就直接复制到老年代。

    老年代回收 Major GC

    进入老年代的数据较少,所以老年代区被占满的速度较慢,所以垃圾回收也不频繁。

    如果老年代也满了,会触发老年代GC,称为Old GC或者 Major GC。

    由于老年代对象一般来说存活次数较长,所以较常采用标记-压缩算法。
    当老年代满时,会触发 Full GC,即对所有"代"的内存进行垃圾回收

    Minor GC比较频繁,Major GC较少。但一般Major GC时,由于老年代对象也可以引用新生代对象,所以先进行一次Minor GC,然后在Major GC会提高效率。可以认为回收老年代的时候完成了一次FullGC。
    所以可以认为 MajorGC = FullGC

    Minor GC 触发条件:当eden区满了触发
    
    Full GC 触发条件:
        老年代满了
        System.gc()手动调用。不推荐
        
    年轻代:
        存活时长低
        适合复制算法
        
    老年代:
        区域大,存活时长高
        适合标记压缩算法
    

    java 内存调整相关参数

    JVM 内存常用相关参数

    Java 命令行参考文档: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
    

    帮助:man java

    选项分类

    -选项名称 此为标准选项,所有HotSpot都支持
    -X选项名称 此为稳定的非标准选项
    -XX:选项名称 非标准的不稳定选项,下一个版本可能会取消
    
    参数 说明 举例
    -Xms 设置应用程序初始使用的堆内存大小(年轻代+老年代) -Xms2g
    -Xmx 设置应用程序能获得的最大堆内存早期JVM不建议超过32G,内存管理效率下降 -Xms4g
    -XX:NewSize 设置初始新生代大小 -XX:NewSize=128m
    -XX:MaxNewSize 设置最大新生代内存空间 -XX:MaxNewSize=256m
    -Xmnsize 同时设置-XX:NewSize 和 -XX:MaxNewSize,代替两者 -Xmn1g
    -XX:NewRatio 以比例方式设置新生代和老年代 -XX:NewRatio=2,new/old=1/2
    -XX:SurvivorRatio 以比例方式设置eden和survivor(S0或S1) -XX:SurvivorRatio=6,eden/survivor=6/1,new/survivor=8/1
    -Xss 设置每个线程私有的栈空间大小,依据具体线程大小和数量 -Xss256k

    范例: 查看java的选项帮助

    #查看java命令标准选项
    [root@centos ~]# java
    ...
    #查看java的非标准选项
    [root@centos8 ~]# java -X
    -Xmixed mixed mode execution (default)
    ...
    #查看所有不稳定选项的当前生效值
    [root@centos8 ~]# java -XX:+PrintFlagsFinal
    [Global flags]
    intx ActiveProcessorCount = -1
    ...
    #查看所有不稳定选项的默认值
    [root@centos8 ~]# java -XX:+PrintFlagsInitial
    [Global flags]
    intx ActiveProcessorCount = -1
    ...
    #查看当前命令行的使用的选项设置
    [root@centos8 ~]# java -XX:+PrintCommandLineFlags -XX:InitialHeapSize=15598528 -XX:MaxHeapSize=249576448 -
    XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
    #上面的-XX:+UseParallelGC 说明当前使用Parallel Scavenge + Parallel Old
    

    范例: 查看和指定JVM内存分配

    #默认JVM试图分配最大内存的总内存的1/4,初始化默认总内存为总内存的1/64
    
    #编译生成class文件
    [root@centos8 ~]# javac Heap.java
    
    #通过$CLASSPATH指定类文件路径,否则无法找到类,也可以通过 java -cp /path指定类路径
    [root@centos8 ~]# echo $CLASSPATH
    /usr/local/jdk/lib/:/usr/local/jdk/jre/lib/
    
    [root@centos8 ~]# cp Heap.class /usr/local/jdk/lib
    #查看当前内存默认值
    [root@centos8 ~]# java -XX:+PrintGCDetails Heap
    
    #指定内存空间
    [root@centos8 ~]# java -Xms1024m -Xmx1024m -XX:+PrintGCDetails Heap
    

    范例: 查看OOM

    JDK 工具监控使用情况

    案例1: jvisualvm工具
    范例: 指定参数运行Java程序

    [root@centos8 ~]# java -cp . -Xms512m -Xmx1g HelloWorld
    
    [root@tomcat ~]# java -Xms256m -Xmx512m HelloWorld
    [root@tomcat ~]# java -Xms128m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=200m
    HelloWorld
    hello magedu
    [root@tomcat ~]# jps
    21299 Main
    21418 Jps
    21407 HelloWorld
    
    #将Linux的图形工具显示到windows桌面
    #方法1
    #注意:先在windows上开启Xwindows Server,如Xmanager
    [root@tomcat ~]# export DISPLAY=172.31.0.1:0.0
    
    #方法2:使用 MobaXterm 连接
    [root@tomcat ~]# yum -y install xorg-x11-xauth xorg-x11-fonts-* xorg-x11-fontutils xorg-x11-fonts-Type1
    [root@tomcat ~]# exit
    
    #运行图形工具
    [roottomcat ~]# which jvisualvm
    /usr/local/jdk/bin/jvisualvm
    [root@tomcat ~]# jvisualvm
    

    案例2: 使用 jvisualvm的 Visual GC 插件

    范例: 使用 jvisualvm的 Visual GC 插件 观察 java程序的OOM

    Jprofiler定位OOM的问题原因

    JProfiler是一款功能强大的Java开发分析工具,它可以快速的帮助用户分析出存在的错误,软件还可对需
    要的显示类进行标记,包括了内存的分配情况和信息的视图等

    JProfiler官网:http://www.ej-technologies.com/products/jprofiler/overview.html
    

    下载并安装JProfiler(傻瓜式安装略)后,双击打开前面生成的两个文件java_pid96236.hprof和java_pid96339,可以看到显示,从中分析OOM原因

    Tomcat的JVM参数设置

    默认不指定,-Xmx大约使用了1/4的内存,当前本机内存指定约为1G。
    在bin/catalina.sh中增加一行

    ......
    # OS specific support. $var _must_ be set to either true or false.
    #添加下面一行
    JAVA_OPTS="-server -Xms128m -Xmx512m -XX:NewSize=48m -XX:MaxNewSize=200m"
    cygwin=false
    darwin=false
    ........
    
    -server:VM运行在server模式,为在服务器端最大化程序运行速度而优化
    -client:VM运行在Client模式,为客户端环境减少启动时间而优化
    

    重启 Tomcat 观察

    垃圾收集方式

    按工作模式不同:指的是GC线程和工作线程是否一起运行
    
        独占垃圾回收器:只有GC在工作,STW一直进行到回收完毕,工作线程才能继续执行
    
        并发垃圾回收器:让GC线程垃圾回收某些阶段可以和工作线程一起进行,如:标记阶段并行,回收阶段仍然串行
    
    按回收线程数:指的是GC线程是否串行或并行执行
    
        串行垃圾回收器:一个GC线程完成回收工作
    
        并行垃圾回收器:多个GC线程同时一起完成回收工作,充分利用CPU资源
    

    调整策略

    对JVM调整策略应用极广
    在WEB领域中Tomcat等
    在大数据领域Hadoop生态各组件
    在消息中间件领域的Kafka等
    在搜索引擎领域的ElasticSearch、Solr等
    

    注意: 在不同领域和场景对JVM需要不同的调整策略

    减少 STW 时长,串行变并行

    减少 GC 次数,要分配合适的内存大小

    一般情况下,大概可以使用以下原则:

    客户端或较小程序,内存使用量不大,可以使用串行回收
    对于服务端大型计算,可以使用并行回收
    大型WEB应用,用户端不愿意等待,尽量少的STW,可以使用并发回收
    

    垃圾回收器

    常用垃圾回收器

    按分代设置不同垃圾回收器:

    新生代

    新生代串行收集器Serial:单线程、独占式串行,采用复制算法,简单高效但会造成STW
    
    新生代并行回收收集器PS(Parallel Scavenge):多线程并行、独占式,会产生STW, 使用复制算法
    关注调整吞吐量,此收集器关注点是达到一个可控制的吞吐量
    吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),比如虚拟机总共运行100分钟,其中垃圾回收花掉1分钟,那吞吐量就是99%。高吞吐量可以高效率利用CPU时间,尽快完成运算任务,主要适合在后台运算而不需要太多交互的任务。
    除此之外,Parallel Scavenge 收集器具有自适应调节策略,它可以将内存管理的调优任务交给虚
    拟机去完成。自适应调节策略也是Parallel Scavenge与 ParNew 收集器的一个重要区别。和ParNew不同,PS不可以和老年代的CMS组合
    
    新生代并行收集器ParNew:就是Serial 收集器的多线程版,将单线程的串行收集器变成了多线程并
    行、独占式,使用复制算法,相当于PS的改进版
    经常和CMS配合使用,关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,适合需要与用户交互的程序,良好的响应速度能提升用户体验
    

    老年代:

    老年代串行收集器Serial Old:Serial Old是Serial收集器的老年代版本,单线程、独占式串行,回收算法使用标记压缩
    
    老年代并行回收收集器Parallel Old:多线程、独占式并行,回收算法使用标记压缩,关注调整吞吐量
    Parallel Old收集器是Parallel Scavenge 收集器的老年代版本,这个收集器是JDK1.6之后才开始提
    供,从HotSpot虚拟机的垃圾收集器的图中也可以看出,Parallel Scavenge 收集器无法与CMS收集
    器配合工作,因为一个是为了吞吐量,一个是为了客户体验(也就是暂停时间的缩短)
    CMS (Concurrent Mark Sweep并发标记清除算法) 收集器
        在某些阶段尽量使用和工作线程一起运行,减少STW时长(200ms以内), 提升响应速度,是互联网服务端BS系统上较佳的回收算法
        
        分为4个阶段:初始标记、并发标记、重新标记、并发清除,在初始标记、重新标记时需要STW。
    
        初始标记:此过程需要STW(Stop The Word),只标记一下GC Roots能直接关联到的对象,速度很快。
        
        并发标记:就是GCRoots进行扫描可达链的过程,为了找出哪些对象需要收集。这个过程远远慢于初始标记,但它是和用户线程一起运行的,不会出现STW,所有用户并不会感受到。
        
        重新标记:为了修正在并发标记期间,用户线程产生的垃圾,这个过程会比初始标记时间稍微长一点,但是也很快,和初始标记一样会产生STW。
        
        并发清理:在重新标记之后,对现有的垃圾进行清理,和并发标记一样也是和用户线程一起运行的,耗时较长(和初始标记比的话),不会出现STW。
        
    由于整个过程中,耗时最长的并发标记和并发清理都是与用户线程一起执行的,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
    

    以下收集器不再按明确的分代单独设置

    G1(Garbage First)收集器
        是最新垃圾回收器,从JDK1.6实验性提供,JDK1.7发布,其设计目标是在多处理器、大内存服务器端提供优于CMS收集器的吞吐量和停顿控制的回收器。JDK9将G1设为默认的收集器,建议 JDK9版本以后使用。
        
        基于标记压缩算法,不会产生大量的空间碎片,有利于程序的长期执行。
        
        分为4个阶段:初始标记、并发标记、最终标记、筛选回收。并发标记并发执行,其它阶段STW只有GC线程并行执行。
        
        G1收集器是面向服务端的收集器,它的思想就是首先回收尽可能多的垃圾(这也是Garbage-First名字的由来)
        
        G1能充分的利用多CPU,多核环境下的硬件优势,使用多个CPU来缩短STW停顿的时间(10ms以内)。
        
        可预测的停顿:这是G1相对于CMS的另一大优势,G1和CMS一样都是关注于降低停顿时间,但是G1能够让使用者明确的指定在一个M毫秒的时间片段内,消耗在垃圾收集的时间不得超过N毫秒。
        
        通过此选项指定: +UseG1GC
        
    ZGC收集器: 减少SWT时长(1ms以内), 可以PKC++的效率,目前实验阶段
    
    Shenandoah收集器: 和ZGC竞争关系,目前实验阶段
    
    Epsilon收集器: 调试 JDK 使用,内部使用,不用于生产环境
    

    JVM 1.8 默认的垃圾回收器:PS + ParallelOld,所以大多数都是针对此进行调优

    垃圾收集器设置

    优化调整Java 相关参数的目标: 尽量减少FullGC和STW,
    通过以下选项可以单独指定新生代、老年代的垃圾收集器

    -server 指定为Server模式,也是默认值,一般使用此工作模式
    -XX:+UseSerialGC
        运行在Client模式下,新生代是Serial, 老年代使用SerialOld
    -XX:+UseParNewGC
        新生代使用ParNew,老年代使用SerialOld
    -XX:+UseParallelGC
        运行于server模式下,新生代使用Serial Scavenge,老年代使用SerialOld
    -XX:+UseParallelOldGC
        新生代使用Paralell Scavenge, 老年代使用Paralell Old
        
        -XX:ParallelGCThreads=N,在关注吞吐量的场景使用此选项增加并行线程数
    -XX:+UseConcMarkSweepGC
        新生代使用ParNew, 老年代优先使用CMS,备选方式为Serial Old
        
        响应时间要短,停顿短使用这个垃圾收集器
        
        -XX:CMSInitiatingOccupancyFraction=N,N为0-100整数表示达到老年代的大小的百分比多少触发回收,默认68
        
        -XX:+UseCMSCompactAtFullCollection开启此值,在CMS收集后,进行内存碎片整理
        -XX:CMSFullGCsBeforeCompaction=N设定多少次CMS后,进行一次内存碎片整理
        -XX:+CMSParallelRemarkEnabled 降低标记停顿
    

    范例:

    -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5
    

    范例: 查看默认模式

    [root@centos8 ~]# java | & grep '-server'
    -server to select the "server" VM
    The default VM is server.
    
    [root@centos8 ~]# tail -n 2 /usr/local/jdk/jre/lib/amd64/jvm.cfg
    -server KNOWN
    -client IGNORE
    

    范例: 指定垃圾回收设置

    #将参数加入到bin/catalina.sh中,重启观察Tomcat status。老年代已经使用CMS
    [root@tomcat ~]# vim /usr/local/tomcat/bin/catalina.sh
    ......
    # OS specific support. $var _must_ be set to either true or false.
    JAVA_OPTS="-server -Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5"
    cygwin=false
    darwin=false
    os400=false
    .......
    [root@tomcat ~]# systemctl restart tomcat
    

    开启垃圾回收输出统计信息,适用于调试环境的相关选项

    -XX:+PrintGC 输出GC信息
    -XX:+PrintGCDetails 输出GC详细信息
    -XX:+PrintGCTimeStamps 与前两个组合使用,在信息上加上一个时间戳
    -XX:+PrintHeapAtGC 生成GC前后椎栈的详细信息,日志会更大
    
    注意: 以上适用调试环境,生产环境请移除这些参数,否则有非常多的日志输出
    

    JAVA参数总结

    参数 名称含义 默认值 xx
    -Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
    -Xmx 最大堆大小 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制
    -Xmn 年轻代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的Newgen是不同的。整个堆大小=年轻代大小 +年老代大小 +持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
    -XX:NewSize 设置年轻代大小(for 1.3/1.4)
    -XX:MaxNewSize 年轻代最大值(for 1.3/1.4)
    -XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64
    -XX:MaxPermSize 设置持久代最大值 物理内存的1/4
    -Xss 每个线程的堆栈大小 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右 一般小的应用, 如果栈不是很深, 应该是128k够用的大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
    参数 名称含义 默认值 xxx
    -XX:ThreadStackSize Thread Stack Size (0 means use default stacksize) [Sparc: 512; Solarisx86: 320 (was 256 prior in 5.0 and earlier); Sparc 64bit: 1024; Linux amd64:1024 (was 0 in 5.0 andearlier); all others 0.]
    -XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
    -XX:SurvivorRatio Eden区与Survivor区的大小比值 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
    -XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响
    Perm的大小 =128m
    XX:+UseFastAccessorMethods 原始类型的快速优化
    -XX:+DisableExplicitGC 关闭System.gc() 这个参数需要严格的测试
    -XX:MaxTenuringThreshold 垃圾最大年龄 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率该参数只有在串行GC时才有效
    -XX:+AggressiveOpts 加快编译
    -XX:+UseBiasedLocking 锁机制的性能改善
    -Xnoclassgc 禁用垃圾回收
    -XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 可达的对象在上次被引用后将保留一段时间。 缺省值是堆中每个空闲兆字节的生命周期的一秒钟
    参数 名称含义 默认值 xxx
    -XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配 0 单位字节 新生代采用Parallel Scavenge GC时无效另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.
    -XX:TLABWasteTargetPercent TLAB占eden区的百分比 1%
    -XX:+CollectGen0First FullGC时是否先YGC false

    并行收集器相关参数

    -XX:+UseParallelGC Full GC采用parallel MSC 选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集
    -XX:+UseParNewGC 设置年轻代为并行收集 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
    -XX:ParallelGCThreads 并行收集器的线程数 此值最好配置与处理器数目相等 同样适用于CMS
    -XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集(ParallelCompacting) 这个是JAVA 6出现的参数选项
    -XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间) 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值.
    -XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的Survivor区比例 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开.
    -XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比 公式为1/(1+n)
    -XX:+ScavengeBeforeFullGC Full GC前调用YGC true

    CMS相关参数

    -XX:+UseConcMarkSweepGC 使用CMS内存收集 测试中配置这个以后,-XX:NewRatio=4的配置可能失效,所以,此时年轻代大小最好用-Xmn设置
    -XX:+AggressiveHeap 试图是使用大量的物理内存 长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要256MB内存 大量的CPU/内存, (在1.4.1在4CPU的机器上已经显示有提升)
    -XX:CMSFullGCsBeforeCompaction 多少次后进行内存压缩 由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生"碎片",使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理.
    -XX:+CMSParallelRemarkEnabled 降低标记停顿
    -XX+UseCMSCompactAtFullCollection 在FULLGC的时候,对年老代的压缩 CMS是不会移动内存的, 因此, 这个非常容易产生碎片,导致内存不够用, 因此, 内存的压缩这个时候就会被启用。增加这个参数是个好习惯。 可能会影响性能,但是可以消除碎片
    -XX:+UseCMSInitiatingOccupancyOnly 使用手动定义初始化定义开始CMS收集 禁止hostspot自行触发CMS GC
    -XX:CMSInitiatingOccupancyFraction=70 使用cms作为垃圾回收使用70%后开始CMS收集 92
    -XX:+UseConcMarkSweepGC 使用CMS内存收集 测试中配置这个以后,-XX:NewRatio=4的配置可能失效,所以,此时年轻代大小最好用-Xmn设置
    -XX:CMSInitiatingPermOccupancyFraction 设置PermGen使用到达多少比率时触发 92
    -XX:+CMSIncrementalMode 设置为增量模式 用于单CPU情况
    -XX:+CMSClassUnloadingEnabled

    辅助信息

    -XX:+PrintGC header 2 xx 输出形式:[GC 118250K->113543K(130112K),0.0094143 secs] [Full GC121376K->10414K(130112K),0.0650971 secs]
    -XX:+PrintGCDetails 输出形式:[GC [DefNew:8614K->781K(9088K),0.0123035 secs] 118250K->113543K(130112K),0.0124633 secs] [GC[DefNew: 8614K->8614K(9088K), 0.0000665secs] 121376K->10414K(130112K),0.0436268 secs]
    -XX:+PrintGCTimeStamps
    -XX:+PrintGC:PrintGCTimeStamps 可与-XX:+PrintGC -XX:+PrintGCDetails混合使用输出形式:11.851: [GC98328K->93620K(130112K),0.0082960 secs]
    -XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间.可与上面混合使用 输出形式:Total time forwhich application threadswere stopped: 0.0468229seconds
    XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用 输出形式:Application time:0.5291524 seconds
    -XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息
    -Xloggc:filename 把相关日志信息记录到文件以便分析. 与上面几个配合使用
    -XX:+PrintGC 输出形式:[GC 118250K->113543K(130112K),0.0094143 secs] [Full GC121376K->10414K(130112K),0.0650971 secs]
    -XX:+PrintClassHistogram garbagecollectsbeforeprintingthehistogram.
    -XX:+PrintTLAB 查看TLAB空间的使用情况
    XX:+PrintTenuringDistribution 查看每次minor GC后新的存活周期的阈值

    JVM相关工具

    JVM 工具概述

    $JAVA_HOME/bin下

    命令 说明
    jps 查看所有jvm进程
    jinfo 查看进程的运行环境参数,主要是jvm命令行参数
    jstat 对jvm应用程序的资源和性能进行实时监控
    jstack 查看所有线程的运行状态
    jmap 查看jvm占用物理内存的状态
    jhat +UseParNew
    jconsole 图形工具
    jvisualvm 图形工具

    jps

    JVM 进程状态工具

    格式

    jps:Java virutal machine Process Status tool,
    jps [-q] [-mlvV] [<hostid>]
    -q:静默模式;
    -v:显示传递给jvm的命令行参数;
    -m:输出传入main方法的参数;
    -l:输出main类或jar完全限定名称;
    -v:显示通过flag文件传递给jvm的参数;
    [<hostid>]:主机id,默认为localhost;
    

    范例

    #显示java进程
    [root@tomcat ~]# jps
    22357 Jps
    21560 Main
    21407 HelloWorld
    #详细列出当前Java进程信息
    [root@tomcat ~]# jps -l -v
    

    jinfo

    输出给定的java进程的所有配置信息
    格式:

    jinfo [option] <pid>
    -flags:打印 VM flags
    -sysprops:to print Java system properties
    -flag <name>:to print the value of the named VM flag
    

    范例:

    #先获得一个java进程ID,然后jinfo
    [root@tomcat ~]# jps
    22357 Jps
    21560 Main
    21407 HelloWorld
    [root@tomcat ~]# jinfo 21407
    Attaching to process ID 21407, please wait...
    

    jstat

    输出指定的java进程的统计信息
    格式:

    jstat -help|-options
    jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
    [<interval> [<count>]]
    interval:时间间隔,单位是毫秒;
    count:显示的次数;
    #返回可用统计项列表
    # jstat -options
    -class:class loader
    -compiler:JIT
    -gc:gc
    -gccapacity:统计堆中各代的容量
    -gccause:
    -gcmetacapacity
    -gcnew:新生代
    -gcnewcapacity
    -gcold:老年代
    -gcoldcapacity
    -gcutil
    

    范例:

    [root@tomcat ~]# jstat -gc 21407
    
    S0C:S0区容量
    YGC:新生代的垃圾回收次数
    YGCT:新生代垃圾回收消耗的时长;
    FGC:Full GC的次数
    FGCT:Full GC消耗的时长
    GCT:GC消耗的总时长
    #3次,一秒一次
    [root@tomcat ~]# jstat -gcnew 21407 1000 3
    S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
    8704.0 8704.0 1563.6 0.0 15 15 4352.0 69952.0 62794.3 2 0.050
    8704.0 8704.0 1563.6 0.0 15 15 4352.0 69952.0 62794.3 2 0.050
    8704.0 8704.0 1563.6 0.0 15 15 4352.0 69952.0 63074.5 2 0.050
    

    jstack

    程序员常用堆栈情况查看工具

    jstack:查看指定的java进程的线程栈的相关信息
    格式:

    jstack [-l] <pid>
    jstack -F [-m] [-l] <pid>
    -l:long listings,会显示额外的锁信息,因此,发生死锁时常用此选项
    -m:混合模式,既输出java堆栈信息,也输出C/C++堆栈信息
    -F:当使用"jstack -l PID"无响应,可以使用-F强制输出信息
    

    范例:

    #先获得一个java进程PID,然后jinfo
    [root@tomcat ~]# jstack -l 21407
    

    jmap

    Memory Map, 用于查看堆内存的使用状态

    #查看进程堆内存情况
    [root@tomcat ~]# jmap -heap 21407
    Attaching to process ID 21407, please wait...
    

    jhat

    Java Heap Analysis Tool 堆分析工具
    格式

    jmap [option] <pid>
    #查看堆空间的详细信息:
    jmap -heap <pid>
    

    范例:

    [root@t1 ~]# jmap -heap 96334
    Attaching to process ID 96334, please wait...
    
    #查看堆内存中的对象的数目:
    jmap -histo[:live] <pid>
    live:只统计活动对象;
    #保存堆内存数据至文件中,而后使用jvisualvm或jhat进行查看:
    jmap -dump:<dump-options> <pid>
    dump-options:
    live dump only live objects; if not specified, all objects in the heap are
    dumped.
    format=b binary format
    file=<file> dump heap to <file>
    

    jconsole

    图形化工具,可以用来查看Java进程信息
    JMX(Java Management Extensions,即Java管理扩展)是一个为JAVA应用程序、设备、系统等植入管
    理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无
    缝集成的系统、网络和服务管理应用。
    JMX最常见的场景是监控Java程序的基本信息和运行情况,任何Java程序都可以开启JMX,然后使用
    JConsole或Visual VM进行预览。

    #为Java程序开启JMX很简单,只要在运行Java程序的命令后面指定如下命令即可
    -Djava.rmi.server.hostname=172.31.0.100 #指定自已监听的IP
    -Dcom.sun.management.jmxremote.port=1000 #指定监听的PORT
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false
    

    在 tomcat 开启远程 JMX 支持 Zabbix 监控,如下配置

    [root@tomcat ~]# vim /usr/local/tomcat/bin/catalina.sh
    CATALINA_OPTS="$CATALINA__OPTS
    -Dcom.sun.management.jmxremote #启用远程监控JMX
    -Dcom.sun.management.jmxremote.port=XXXXX #默认启动的JMX端口号,要和
    zabbix添加主机时候的端口一致即可
    -Dcom.sun.management.jmxremote.authenticate=false #不使用用户名密码
    -Dcom.sun.management.jmxremote.ss1=false #不使用ssl认证
    -Djava.rmi.server.hostname=<JAVA主机IP>" #tomcat主机自己的IP地址,不要写zabbix服务器的地址
    

    使用Jconsle通过JMX查看Java程序的运行信息

    范例: 开启远程JMX功能

    [root@tomcat ~]# vim /usr/local/tomcat/bin/catalina.sh
    CATALINA_OPTS="$CATALINA__OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=12345 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=172.31.0.101"
    # OS specific support. $var _must_ be set to either true or false.
    .......
    [root@tomcat ~]# systemctl restart tomcat
    

    Tomcat 性能优化常用配置

    内存空间优化

    JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "
    -server:服务器模式
    -Xms:堆内存初始化大小
    -Xmx:堆内存空间上限
    -XX:NewSize=:新生代空间初始化大小
    -XX:MaxNewSize=:新生代空间最大值
    

    生产案例:

    [root@centos8 ~]# vim /usr/local/tomcat/bin/catalina.sh
    JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"
    #一台tomcat服务器并发连接数不高,生产建议分配物理内存通常4G到8G较多,如果需要更多连接,一般会利用虚拟化技术实现多台tomcat
    

    线程池调整

    [root@centos8 ~]# vim /usr/local/tomcat/conf/server.xml
    ......
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
    redirectPort="8443" />
    ......
    

    常用属性:

    connectionTimeout :连接超时时长,单位ms
    maxThreads:最大线程数,默认200
    minSpareThreads:最小空闲线程数
    maxSpareThreads:最大空闲线程数
    acceptCount:当启动线程满了之后,等待队列的最大长度,默认100
    URIEncoding:URI 地址编码格式,建议使用 UTF-8
    enableLookups:是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行
    
    compression:是否启用传输压缩机制,建议 "on",CPU和流量的平衡
        compressionMinSize:启用压缩传输的数据流最小值,单位是字节
        compressableMimeType:定义启用压缩功能的MIME类型text/html, text/xml, text/css,text/javascript
    

    Java压力测试工具

    PerfMa 致力于打造一站式IT系统稳定性保障解决方案,专注于性能评测与调优、故障根因定位与解决,
    为企业提供一系列技术产品与专家服务,提升系统研发与运行质量。

    #社区产品
    https://opts.console.perfma.com/
    
  • 相关阅读:
    Atitit 人脸识别 眼睛形态 attilax总结
    Atitit 手机号码选号 规范 流程 attilax总结 v2 r99.docx
    atitit 板块分类 上市公司 龙头企业公司 列表 attilax总结.docx
    Atititi atiitt eam pam资产管理 购物表去年.xlsx
    使用cmd查看电脑连接过的wifi密码(一)
    常见十大web攻击手段 悟寰轩
    常见web攻击方式 悟寰轩
    【MYSQL数据库】MYSQL学习笔记mysql分区基本操作 悟寰轩
    Filter及FilterChain的使用详解 悟寰轩
    启动tomcat spring初始化两次问题(eg:@PostConstruct) 悟寰轩
  • 原文地址:https://www.cnblogs.com/xuanlv-0413/p/15168183.html
Copyright © 2011-2022 走看看