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/
    
  • 相关阅读:
    NHibernate使用
    成为专业程序员路上用到的各种优秀资料、神器及框架
    SignalR
    sql 语句 事务
    entity framework 数据加载三种方式的异同(延迟加载,预加载,显示加载)
    获取前一个页面的地址
    给button按钮加回车事件
    单元测试
    教学资源网站整理
    Go资源
  • 原文地址:https://www.cnblogs.com/xuanlv-0413/p/15168183.html
Copyright © 2011-2022 走看看