精华内容
下载资源
问答
  • JVM调优步骤

    2019-11-05 18:50:23
    JVM调优主要就是调整以下两个指标 停顿时间:垃圾收集器做垃圾回收中断应用执行的时间 吞吐量:垃圾收集的时间和总时间的占比: 1/(1+n) ;吞吐量为 1-1/(1+n); -XX:GCTimeRatio=99 GC调优的步骤 1、打印gc日志 ...

    调优一般指的是上线前的参数调优。

    JVM调优主要就是调整以下两个指标

    停顿时间:垃圾收集器做垃圾回收中断应用执行的时间

    吞吐量:垃圾收集的时间和总时间的占比: 1/(1+n) ;吞吐量为 1-1/(1+n); -XX:GCTimeRatio=99

     

    GC调优的步骤

    1、打印gc日志

    -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -xloggc:./gc.log

    Tomcat 可以直接加载 JAVA_OPTS 变量里

    2、分析日志得到关键性指标 可以用 gceasy gcview等工具

    3、分析GC原因,调优jvm参数

     

    1、Parallel Scavenge 收集器(默认)

    分析parallel-gc.log

    第一次调优:设置MetaSpace 大小,增大元空间大小, -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M

    第二次调优:增大年轻代动态扩容增量(默认20%);可以减少YGC     -XX:YoungGenerationSizeIncrement=30

    2、配置CMS

    -XX:+UseConcMarkSweepGC

    分析 gc-cms.log

    3、配置g1收集器

    -XX:+UseG1GC

    分析 g1-gc.log

     G1内部,前面提到的混合gc是非常重要的释放内存机制,避免了g1出现region没有可用的情况,否则就会触发full gc,cms、parallel、serial gc都需要通过fullgc去压缩老年代并在这个过程中扫描整个老年代,g1的fullgc算法和serial gc的收集一致,当一个full gc发生时候,整个java堆执行一个完整的压缩,这样确保了最大的空余内存可用,g1的fullgc是个单线程,会引起一个长时间的停顿时间,g1的设计目标是减少fullgc,满足应用性能指标。

    查看发生mixed gc的阈值:

    jinfo -flag initiatingHeapOccupancyPercent 进程ID

    调优

    第一次调优:设置ma'tespace的大小,增大元空间的大小,

    -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=64m

    第二次调优:添加吞吐量和停顿时间参数

    -XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=10

     

    GC常用参数

    堆栈设置

    -xss:每个线程的栈大小

    -xms:初始堆大小,默认物理内存的 1/64

    -xmx:最大堆大小,默认物理内存的 1/4

    -xmn:新生代大小

    -XX:NewSize 设置新生代的初始大小

    -XX:NewRatio 默认2 表示新生代占年老代的1/2,占整个堆内存的1/3

    -XX:SurvivorRatio 默认8 表示一个s区域占1/8的eden内存 即1/10的新生代内存

    -XX:MetaspaceSize 设置元空间的大小

    -XX:MaxMetaspaceSize 设置元空间的最大允许大小默认不受限制 jvm metaspace 会进行动态扩展

     

    垃圾回收统计信息

    -XX:+PrintGC

    -XX:+PrintGCDetails

    -XX:+PrintGCTimeStamps

    -Xloggc:filename

     

    收集器设置

    -XX:+UseSerialGC

    -XX:+UseParallelGC

    -XX:+UseParallelOldGC

    -XX:+UseParNewGC

    -XX:+UseConcMarkSweepGC

    -XX:+UseG1GC

     

     

    并行收集器设置

    -XX:ParallelGCThreads:设置用于垃圾回收的线程数

    -XX:MaxGCPauseMillis:设置并行收集最大暂停时间

    -XX:GCTimeRatio 垃圾回收占比

     

    CMS收集器设置

    -XX:+UseConcMarkSweepGC 设置cms并发收集器

    -XX:+CMSIncrementalModel 设置为增量模式 适用于单cpu情况

    -XX:ParallelGCThreads 并行收集线程数

    -XX:CMSFullGCsBeforeCpmpaction 设置进行多少次cms垃圾回收后,进行一次内存压缩

    -XX:CMSClassUnloadingEnabled 允许对类的元数据进行回收

    -XX:UseCMSInitiatingOccupancyOnly 表示只在到达阈值的时候才进行cms回收

    -XX:CMSInitiatingOccupancyFraction 设置cms收集器在老年代空间被使用多少后触发

    -XX:+UseCMSCompactAtFullCollection 设置完成垃圾收集后是否要进行一次内存碎片的整理

     

    G1收集器设置

    -XX:+UseG1GC

    -XX:ParalledGCThreads

    -XX:G1HeapRegionSize 指定分区大小 (1M-32M,而且必须是2的幂),默认将整个堆划分为2048个分区

    -XX:GCTimeRatio 0-100的整数 默认9 值为n则为系统将花费不超过1/(1+n)的时间用于垃圾收集

    -XX:MaxGCPauseMillis 目标暂停时间 默认 200ms

    -XX:G1NesSizePercent 新生代内存初始空间 默认为整个堆的5%

    -XX:G1MaxNewSizePercent 新生代最大内存大小

    -XX:TargetSurvivorRatio s区的填充容量 默认50%

    -XX:MaxTenuringThreshold 最大任期阈值 默认15

    -XX:IintiatingfHeapOccupancyPercent  老年代占用空间超过整堆比HOP阈值(45%默认)超过则执行混合收集

    -XX:G1HeapWastePercent 堆废物的百分比

    -XX:G1MixedGCCountTarget 参数混合周期的的最大总次数 默认8

     

     

    展开全文
  • 深入浅出JVM调优,看完你就懂

    万次阅读 多人点赞 2019-08-12 13:50:52
    深入浅出JVM调优 基本概念: JVM把内存区分为堆区(heap)、栈区(stack)和方法区(method)。由于本文主要讲解JVM调优,因此我们可以简单的理解为,JVM中的堆区中存放的是实际的对象,是需要被GC的。其他的都无需GC。 ...

     

     

    深入浅出JVM调优

    基本概念:

    JVM把内存区分为堆区(heap)、栈区(stack)和方法区(method)。由于本文主要讲解JVM调优,因此我们可以简单的理解为,JVM中的堆区中存放的是实际的对象,是需要被GC的。其他的都无需GC。

    下图文JVM的内存模型

    深入浅出JVM调优,看完你就懂

     

    从图中我们可以看到,

    1、JVM实质上分为三大块,年轻代(YoungGen),年老代(Old Memory),及持久代(Perm,在Java8中被取消,我们不做深入介绍)。

    2、垃圾回收GC,分为2种,一是Minor GC,可以可以称为YGC,即年轻代GC,当Eden区,还有一种称为Major GC,又称为FullGC。

    3、GC原理:

    我们可以看到年轻代包括Eden区(对象刚被new出来的时候,放到该区),S0和S1,是幸存者1区和幸存者2区,从名字可以看出,是当发生YGC,没有被任何其他对象所引用的对象将会从内存中被清除,还被其他对象引用的则放到幸存者区。当发生多次YGC,在S0、S1区多次没有被清楚的对象,则会被移到老年代区域。当老年代区域被占满的时候,则会发送FullGC。

    无论是YGC或是FullGC,都会导致stop-the-world,即整个程序停止一些事务的处理,只有GC进程允许以进行垃圾回收,因此如果垃圾回收时间较长,部分web或socket程序,当终端连接的时候会报connetTimeOut或readTimeOut异常,

    4、从JVM调优的角度来看,我们应该尽量避免发生YGC或FullGC,或者使得YGC和FullGC的时间足够的短。

    JMV调优的准备工作。

    1)、配置jstatd的远程RMI服务(当我们要看远程服务器上JAVA程序的GC情况的时候,需要执行此步骤),允许JVM工具查看JVM使用情况。

    将如下的代码存为文件 jstatd.all.policy,放到JAVA_HOME/bin中,其内容如下:不知道JAVA_HOME目录的,可以执行 which java查看。

    grant codebase "file:${java.home}/../lib/tools.jar" {

    permission java.security.AllPermission;

    };

    执行命令jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=10.27.20.38 &

    (10.27.20.38为你服务器的ip地址,&表示用守护线程的方式运行)

    2)、执行C:\glassfish4\jdk7\bin\jvisualvm.exe 打开JVM控制台。

    工具--插件--中找到Visual GC插件进行安装.

    3)、对要执行java程序进行调优,以 c1000k.jar为例,在该jar包所在目录下建立一个start.sh文件,文件内容如下。

    java -server -Xms4G -Xmx4G -Xmn2G -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar c1000k.jar&

    通过这样的配置,使用JVM控制台即可查看JVM(CPU/内存)及垃圾回收的情况。

    4)、控制台配置

    打开\jvisualvm.exe,远程---添加远程主机---输入远程IP----添加JMX连接

    深入浅出JVM调优,看完你就懂

     

    5)、下面开始正式的JVM调优。

    5.1 JVM调优核心为调整年轻代、年老大的内存空间大小及使用GC发生器的类型等。回到上文的start.sh文件内容,我们来分下:

    java -server -Xms4G -Xmx4G -Xmn2G -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar c1000k.jar&

    这台机器是一个4G内存的机器,因此:

    -Xms4G 是指: JVM启动时整个堆(包括年轻代,年老代)的初始化大小。

    -Xmx4G 是指: JVM启动时整个堆的最大值。

    -Xmn2G是指:年轻代的空间大小,剩下的是年老代的空间。

    -XX:SurvivorRatio=1是指:年轻代空间中2个Survivor空间与Eden空间的大小比例。此处为1:1:1,算法如下:比如整个年轻代空间为2G,如果比例为1,那么2/3,则S0/S1/Eden的空间大小是同样的,为666M。

    该值不设置,则JDK默认为比例为8,那么是1:1:8,通过上面的算法可以得出S0/S1的大小。我们可以看到官方通过增大Eden区的大小,来减少YGC发生的次数,但有时我们发现,虽然次数减少了,但Eden区满

    的时候,由于占用的空间较大,导致释放缓慢,此时stop-the-world的时间较长,因此需要按照程序情况去调优。

    -XX:+UseConcMarkSweepGC是指:使用GC的回收类型。这里是CMS类型,JDK1.7以后推荐使用+UseG1GC,被称为G1类型(或Garbage First)的回收器。

    5.2当我们设定好start.sh的参数值后,执行./start.sh此时就启动了。

    我们可以通过jvisualvm.exe中的Visual GC插件查看GC的图形,我们也可以再服务器上执行:jstat -gc 15016 1000,看到每1秒钟java进程号为15016的GC回收情况。

    [root@yxdevapp04 c1000k]# jstat -gc 15016 1000

    S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT

    699008.0 699008.0 29980.4 0.0 699136.0 116881.6 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

    699008.0 699008.0 29980.4 0.0 699136.0 118344.8 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

    699008.0 699008.0 29980.4 0.0 699136.0 119895.5 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

    699008.0 699008.0 29980.4 0.0 699136.0 121383.1 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

    解释如下:

    S0C 是指:Survivor0区的分配空间

    S0U 是指:Survivor1区的已经使用的空间

    EC是指:Eden区所使用的空间

    EU是指:Eden区当前使用的空间

    OC是指:老年代分配的空间

    OU是指:老年代当前使用的空间

    PC是指:持久待分配的空间

    PU是指:持久待当前使用的空间

    YGC是指:年轻代发生的次数,这里是354次

    YGCT是指:年轻代发送的总时长,这里是54.272秒,因此每次年轻代发生GC,即平均每次stop-the-world的时长为54.272/354=0.153秒。

    FGC是指:年老代回收的次数,或者成为FullGC的次数。

    FGCT是指:年老代发生回收的总时长。

    GCT是指:包括年轻代YGC及年老代FGC的总时间长。

    通常结合图形或数据,我们可以看到当EU即将等于EC的时候,此时发生YGC,因此YGC次数+1,YGCT时间增加。

    经过实际的调优测试我们发现,当发生YGC的时候,如果S0U或S1U区如果有任意一个区域为0的时候,此时YGC的速度很快,相反如果S0U或者S1U中都有数据,或相对满的时候,此时YGC的时间边长,这就是因为S0/S1及Eden区的比例问题导致的。

    5.3经过一定时间的调优,我们基本上可以使得YGC的次数非常少,时间非常快,很长时间,数天都不会发生FGC,此时JVM的调优算是一个好的结果。

    5.4 在MAC电脑上可以通过jconsole调出图形化分析工具。

    展开全文
  • JVM-JVM调优

    2019-10-11 17:25:45
    讲述jvm调优基本策略。

    目录

    目标

    why

    情况一:G1

    fullGC

    调整延迟

    调整吞吐量

    情况二:年轻代并行,年老代cms

    更高吞吐量

    更小延迟

    OOM

    基本操作步骤


    目标

    讲述jvm调优基本策略。

    why

    jvm调优目标:更高的吞吐量和更小的延迟。

    前提

    调整JVM配置之前,先从代码层面考虑,改进性能。

    情况一:G1

    建议选用G1.

    官方文档

    G1是不需特殊配置即可提供较好表现性的垃圾回收器。与其改变vm配置,不如从应用本身着手。

    fullGC

    老年代空间占用过高,无法继续分配足够空间,导致fullGC,且fullGC很耗时。并发标记阶段执行较长,没能启动空间回收阶段。

    log中的关键字 Full GC (Allocation Failure) 

    解决方式

    • 增加heap大小,可能增加并发标记的耗时
    • 增加执行并发标记的线程数量,-XX:ConcGCThreads
    • 提前执行并发标记
      • -XX:G1ReservePercent 设置保留空间的比例
      • 或者 关闭自适应比例,设置初始占用比例 -XX:-G1UseAdaptiveIHOP  -XX:InitiatingHeapOccupancyPercent

    调整延迟

    分析占用时间

    gc log显示占用时间 Times: user=xx sys=xx, real=xx secs

    user:VM占用

    sys:操作系统占用

    real:pause占用

    sys占用时间较高

    内存空间逐渐增加导致

    • 设置相同-Xms  -Xmx
    • -XX:+AlwaysPreTouch

    Linux操作系统THP特性导致,disable the Transparent Huge Pages feature

    向磁盘文件输出日志时,后台其他任务占用大部分IO带宽,导致log耗时。

    • log输出到独立的磁盘
    • 其他存储方式

    real占用时间较高

    Reference Object Processing Takes Too Long

    • G1更新the referents of Reference Objects 更新引用对象指向真实对象
    • -XX:ReferencesPerThread=0 每个线程负责的引用对象数量,默认1000,设置为0表示使用允许的最多线程数。控制线程数量 -XX:ParallelGCThreads
    • -XX:-ParallelRefProcEnabled

    Young-Only Collections Within the Young-Only Phase Take Too Long

    • 减小年轻代大小 XX:G1NewSizePercent=x -XX:G1MaxNewSizePercent=x

    调整吞吐量

    G1目标是在吞吐量和延迟之间达到平衡。为了提升吞吐量,不仅要减少pause时间,还要降低pause的频率。

    设置最大延迟时间,仍不满足吞吐量要求,则增加年轻代大小

    • -XX:MaxGCPauseMillis
    • -XX:G1NewSizePercent  -XX:G1MaxNewSizePercent 

    减少并发工作数量,concurrent remembered set updates 并发记忆集更新会占用较多cpu资源

    • 将部分工作移动到gc pause阶段执行,配置提高-XX:G1RSetUpdatingPauseTimePercent,G1处理'并发记忆集更新'的时间 占 总gc时间的百分比
    • 完全失效且将所有工作已到gc pause,-XX:-G1UseAdaptiveConcRefinement -XX:G1ConcRefinementGreenZone=2G -XX:G1ConcRefinementThreads=0

    启用large page

    •  -XX:+UseLargePages
    • 查看操作系统如何启用large page

    提前准备heap空间

    • 设置相同-Xms  -Xmx
    • -XX:+AlwaysPreTouch

    设置吞吐量要求

    • -XX:GCTimeRatio=<N>
    • gc占用时间 / 应用占用时间 = 1 / (1 + N)
    • 默认99,即heap占用空间增加前gc占用时间比例为1%

     

    情况二:年轻代并行,年老代cms 

    更高吞吐量

    增大年轻代,可提升吞吐量,但会影响敏捷性、停顿时间

    设置吞吐量要求

    • -XX:GCTimeRatio=<N>
    • gc占用时间 / 应用占用时间 = 1 / (1 + N)
    • 默认99,即heap占用空间增加前gc占用时间比例为1%

    更小延迟

    年轻代设置最大延迟时间

    • -XX:MaxGCPauseMillis=<N> 单位毫秒
    • 可能影响吞吐量

    年老代调整大小 

    OOM

    应用程序导致的问题,进行fix.

    内存分配不满足应用足迹,调整heap.

    基本操作步骤

    使用默认配置,调节Heap大小.

    当Heap调节到某值后,对预期结果已无影响时,调整配置:代占用比例、年轻代最大延迟时间、年轻代吞吐量.

     

     

     

     

     

    展开全文
  • JVM 调优方法

    万次阅读 2019-11-27 18:12:23
    参考 JVM调优总结 在调优之前,我们需要记住下面的原则: 1、多数的Java应用不需要在服务器上进行GC优化; 2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题; 3、在应用上线之前,先考虑将...

    参考 JVM调优总结

    在调优之前,我们需要记住下面的原则:

    1、多数的Java应用不需要在服务器上进行GC优化;

    2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;

    3、在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);

    4、减少创建对象的数量;

    5、减少使用全局变量和大对象;

    6、GC优化是到最后不得已才采用的手段;

    7、在实际使用中,分析GC情况优化代码比优化GC参数要多得多;

    GC优化的目的有两个(http://www.360doc.com/content/13/0305/10/15643_269388816.shtml):

    1、将转移到老年代的对象数量降低到最小;

    2、减少full GC的执行时间;

    为了达到上面的目的,一般地,你需要做的事情有:

    1、减少使用全局变量和大对象;

    2、调整新生代的大小到最合适;

    3、设置老年代的大小为最合适;

    4、选择合适的GC收集器;

    调优总结

    年轻代大小选择

    响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。

    吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

    年老代大小选择

    响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:

    并发垃圾收集信息
    持久代并发收集次数
    传统GC信息
    花在年轻代和年老代回收上的时间比例 ,.减少年轻代和年老代花费的时间,一般会提高应用的效率
    吞吐量优先的应用 : 一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。

    较小堆引起的碎片问题 :因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

    1. -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
    2. -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩

    总结:

    如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 (B)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间

    展望

    以后JVM的调优或许跟多需要针对G1算法进行调优了。

    JVM调优工具

    Jconsole,jProfile,VisualVM

    Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用。对垃圾回收算法有很详细的跟踪。

    JProfiler:商业软件,需要付费。功能强大。

    VisualVM:JDK自带,功能强大,与JProfiler类似。推荐。

    如何调优

    观察内存释放情况、集合类检查、对象树

    上面这些调优工具都提供了强大的功能,但是总的来说一般分为以下几类功能

    堆信息查看

    可查看堆空间大小分配(年轻代、年老代、持久代分配)
    提供即时的垃圾回收功能
    垃圾监控(长时间监控回收情况)
    查看堆内类、对象信息查看:数量、类型等
    对象引用情况查看
    

    有了堆信息查看方面的功能,我们一般可以顺利解决以下问题:

    年老代年轻代大小划分是否合理
    内存泄漏
    垃圾回收算法设置是否合理

    线程监控

    线程信息监控:系统线程数量。 
    线程状态监控:各个线程都处在什么样的状态下
    Dump线程详细信息:查看线程内部运行情况
    死锁检查
    

    热点分析

    CPU热点:检查系统哪些方法占用的大量CPU时间

    内存热点:检查哪些对象在系统中数量最大(一定时间内存活对象和销毁对象一起统计)

    这两个东西对于系统优化很有帮助。我们可以根据找到的热点,有针对性的进行系统的瓶颈查找和进行系统优化,而不是漫无目的的进行所有代码的优化。

    快照

    快照是系统运行到某一时刻的一个定格。在我们进行调优的时候,不可能用眼睛去跟踪所有系统变化,依赖快照功能,我们就可以进行系统两个不同运行时刻,对象(或类、线程等)的不同,以便快速找到问题

    举例说,我要检查系统进行垃圾回收以后,是否还有该收回的对象被遗漏下来的了。那么,我可以在进行垃圾回收前后,分别进行一次堆情况的快照,然后对比两次快照的对象情况。

    内存泄漏检查

    参考 JVM性能调优

    内存泄漏是比较常见的问题,而且解决方法也比较通用,这里可以重点说一下,而线程、热点方面的问题则是具体问题具体分析了。

    内存泄漏一般可以理解为系统资源(各方面的资源,堆、栈、线程等)在错误使用的情况下,导致使用完毕的资源无法回收(或没有回收),从而导致新的资源分配请求无法完成,引起系统错误。
    
    内存泄漏对系统危害比较大,因为他可以直接导致系统的崩溃。
    
    需要区别一下,内存泄漏和系统超负荷两者是有区别的,虽然可能导致的最终结果是一样的。内存泄漏是用完的资源没有回收引起错误,而系统超负荷则是系统确实没有那么多资源可以分配了(其他的资源都在使用)。
    

    年老代堆空间被占满

    异常: java.lang.OutOfMemoryError: Java heap space

    这是最典型的内存泄漏方式,简单说就是所有堆空间都被无法回收的垃圾对象占满,虚拟机无法再在分配新空间。

    解决:

    这种方式解决起来也比较容易,一般就是根据垃圾回收前后情况对比,同时根据对象引用情况(常见的集合对象引用)分析,基本都可以找到泄漏点。
    

    持久代被占满

    异常:java.lang.OutOfMemoryError: PermGen space

    说明:

    Perm空间被占满。无法为新的class分配存储空间而引发的异常。这个异常以前是没有的,但是在Java反射大量使用的今天这个异常比较常见了。主要原因就是大量动态反射生成的类不断被加载,最终导致Perm区被占满。
    
    更可怕的是,不同的classLoader即便使用了相同的类,但是都会对其进行加载,相当于同一个东西,如果有N个classLoader那么他将会被加载N次。因此,某些情况下,这个问题基本视为无解。当然,存在大量classLoader和大量反射类的情况其实也不多。
    

    解决:

    1. -XX:MaxPermSize=16m
    
    2. 换用JDK。比如JRocket。
    

    堆栈溢出

    异常:java.lang.StackOverflowError

    说明:这个就不多说了,一般就是递归没返回,或者循环调用造成

    线程堆栈满

    异常:Fatal: Stack size too small

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

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

    系统内存被占满

    异常:java.lang.OutOfMemoryError: unable to create new native thread

    说明:

    这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,就出现这个异常了。
    

    分配给Java虚拟机的内存愈多,系统剩余的资源就越少,因此,当系统内存固定时,分配给Java虚拟机的内存越多,那么,系统总共能够产生的线程也就越少,两者成反比的关系。同时,可以通过修改-Xss来减少分配给单个线程的空间,也可以增加系统总共内生产的线程数。

    解决:

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

    举例分析 内存泄漏及解决方法 — 参考JVM性能调优

    1.系统崩溃前的一些现象:

    每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4、5s
    FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC
    年老代的内存越来越大并且每次FullGC后年老代没有内存被释放
    之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值。

    2.生成堆的dump文件

    通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。

    3.分析dump文件

    下面要考虑的是如何打开这个3G的堆信息文件,显然一般的Window系统没有这么大的内存,必须借助高配置的Linux。当然我们可以借助X-Window把Linux上的图形导入到Window。我们考虑用下面几种工具打开该文件:

    Visual VM
    IBM HeapAnalyzer
    JDK 自带的Hprof工具
    使用这些工具时为了确保加载速度,建议设置最大内存为6G。使用后发现,这些工具都无法直观地观察到内存泄漏,Visual VM虽能观察到对象大小,但看不到调用堆栈;HeapAnalyzer虽然能看到调用堆栈,却无法正确打开一个3G的文件。因此,我们又选用了Eclipse专门的静态内存分析工具:Mat。

    4.分析内存泄漏

    通过Mat我们能清楚地看到,哪些对象被怀疑为内存泄漏,哪些对象占的空间最大及对象的调用关系。针对本案,在ThreadLocal中有很多的JbpmContext实例,经过调查是JBPM的Context没有关闭所致。

    另,通过Mat或JMX我们还可以分析线程状态,可以观察到线程被阻塞在哪个对象上,从而判断系统的瓶颈。

    5.回归问题

    Q:为什么崩溃前垃圾回收的时间越来越长?

    A:根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据

    Q:为什么Full GC的次数越来越多?

    A:因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收

    Q:为什么年老代占用的内存越来越大?

    A:因为年轻代的内存无法被回收,越来越多地被Copy到年老代

    除了上述内存泄漏外,我们还发现CPU长期不足3%,系统吞吐量不够,针对8core×16G、64bit的Linux服务器来说,是严重的资源浪费。

    在CPU负载不足的同时,偶尔会有用户反映请求的时间过长,我们意识到必须对程序及JVM进行调优。从以下几个方面进行:

    线程池:解决用户响应时间长的问题

    连接池

    JVM启动参数:调整各代的内存比例和垃圾回收算法,提高吞吐量
    程序算法:改进程序逻辑算法提高性能

    1.Java线程池(java.util.concurrent.ThreadPoolExecutor)

    大多数JVM6上的应用采用的线程池都是JDK自带的线程池,之所以把成熟的Java线程池进行罗嗦说明,是因为该线程池的行为与我们想象的有点出入。Java线程池有几个重要的配置参数:
    

    corePoolSize:核心线程数(最新线程数)
    maximumPoolSize:最大线程数,超过这个数量的任务会被拒绝,用户可以通过RejectedExecutionHandler接口自定义处理方式
    keepAliveTime:线程保持活动的时间
    workQueue:工作队列,存放执行的任务
    Java线程池需要传入一个Queue参数(workQueue)用来存放执行的任务,而对Queue的不同选择,线程池有完全不同的行为:

    SynchronousQueue: 一个无容量的等待队列,一个线程的insert操作必须等待另一线程的remove操作,采用这个Queue线程池将会为每个任务分配一个新线程
    LinkedBlockingQueue : 无界队列,采用该Queue,线程池将忽略 maximumPoolSize参数,仅用corePoolSize的线程处理所有的任务,未处理的任务便在LinkedBlockingQueue中排队
    ArrayBlockingQueue: 有界队列,在有界队列和 maximumPoolSize的作用下,程序将很难被调优:更大的Queue和小的maximumPoolSize将导致CPU的低负载;小的Queue和大的池,Queue就没起动应有的作用。
    其实我们的要求很简单,希望线程池能跟连接池一样,能设置最小线程数、最大线程数,当最小数<任务<最大数时,应该分配新的线程处理;当任务>最大数时,应该等待有空闲线程再处理该任务。

    但线程池的设计思路是,任务应该放到Queue中,当Queue放不下时再考虑用新线程处理,如果Queue满且无法派生新线程,就拒绝该任务。设计导致“先放等执行”、“放不下再执行”、“拒绝不等待”。所以,根据不同的Queue参数,要提高吞吐量不能一味地增大maximumPoolSize。
    
    当然,要达到我们的目标,必须对线程池进行一定的封装,幸运的是ThreadPoolExecutor中留了足够的自定义接口以帮助我们达到目标。我们封装的方式是:
    

    以SynchronousQueue作为参数,使maximumPoolSize发挥作用,以防止线程被无限制的分配,同时可以通过提高maximumPoolSize来提高系统吞吐量
    自定义一个RejectedExecutionHandler,当线程数超过maximumPoolSize时进行处理,处理方式为隔一段时间检查线程池是否可以执行新Task,如果可以把拒绝的Task重新放入到线程池,检查的时间依赖keepAliveTime的大小。

    2.连接池(org.apache.commons.dbcp.BasicDataSource)

    在使用org.apache.commons.dbcp.BasicDataSource的时候,因为之前采用了默认配置,所以当访问量大时,通过JMX观察到很多Tomcat线程都阻塞在BasicDataSource使用的Apache ObjectPool的锁上,直接原因当时是因为BasicDataSource连接池的最大连接数设置的太小,默认的BasicDataSource配置,仅使用8个最大连接。
    
    我还观察到一个问题,当较长的时间不访问系统,比如2天,DB上的Mysql会断掉所以的连接,导致连接池中缓存的连接不能用。为了解决这些问题,我们充分研究了BasicDataSource,发现了一些优化的点:
    

    Mysql默认支持100个链接,所以每个连接池的配置要根据集群中的机器数进行,如有2台服务器,可每个设置为60
    initialSize:参数是一直打开的连接数
    minEvictableIdleTimeMillis:该参数设置每个连接的空闲时间,超过这个时间连接将被关闭
    timeBetweenEvictionRunsMillis:后台线程的运行周期,用来检测过期连接
    maxActive:最大能分配的连接数
    maxIdle:最大空闲数,当连接使用完毕后发现连接数大于maxIdle,连接将被直接关闭。只有initialSize < x < maxIdle的连接将被定期检测是否超期。这个参数主要用来在峰值访问时提高吞吐量。
    initialSize是如何保持的?经过研究代码发现,BasicDataSource会关闭所有超期的连接,然后再打开initialSize数量的连接,这个特性与minEvictableIdleTimeMillis、timeBetweenEvictionRunsMillis一起保证了所有超期的initialSize连接都会被重新连接,从而避免了Mysql长时间无动作会断掉连接的问题。

    解决之道

    数据库、文件系统

    在这种方式下,Java应用的内存基本上等于处理一次峰值并发请求所需的内存。数据的获取都在每次请求时从数据库和文件系统中获取。也可以理解为,一次业务访问以后,所有对象都可以进行回收了。

    这是一种内存使用最有效的方式,但是从应用角度来说,这种方式很低效。
    

    内存-硬盘映射

    上面的问题是因为我们使用了文件系统带来了低效。但是如果我们不是读写硬盘,而是写内存的话效率将会提高很多。
    
    数据库和文件系统都是实实在在进行了持久化,但是当我们并不需要这样持久化的时候,我们可以做一些变通——把内存当硬盘使。
    
    内存-硬盘映射很好很强大,既用了缓存又对Java应用的内存使用又没有影响。Java应用还是Java应用,他只知道读写的还是文件,但是实际上是内存。
    
    这种方式兼得的Java应用与缓存两方面的好处。memcached的广泛使用也正是这一类的代表。
    

    同一机器部署多个JVM

    这也是一种很好的方式,可以分为纵拆和横拆。纵拆可以理解为把Java应用划分为不同模块,各个模块使用一个独立的Java进程。而横拆则是同样功能的应用部署多个JVM。
    
    通过部署多个JVM,可以把每个JVM的内存控制一个垃圾回收可以忍受的范围内即可。但是这相当于进行了分布式的处理,其额外带来的复杂性也是需要评估的。另外,也有支持分布式的这种JVM可以考虑,不要要钱哦:)
    

    程序控制的对象生命周期

    这种方式是理想当中的方式,目前的虚拟机还没有,纯属假设。即:考虑由编程方式配置哪些对象在垃圾收集过程中可以直接跳过,减少垃圾回收线程遍历标记的时间。
    

    线程分配

    Java的阻塞式的线程模型基本上可以抛弃了,目前成熟的NIO框架也比较多了。阻塞式IO带来的问题是线程数量的线性增长,而NIO则可以转换成为常数线程。因此,对于服务端的应用而言,NIO还是唯一选择。不过,JDK7中为我们带来的AIO是否能让人眼前一亮呢?我们拭目以待。
    

    其他的JDK

    本文说的都是Sun的JDK,目前常见的JDK还有JRocket和IBM的JDK。其中JRocket在IO方面比Sun的高很多,不过Sun JDK6.0以后提高也很大。而且JRocket在垃圾回收方面,也具有优势,其可设置垃圾回收的最大暂停时间也是很吸引人的。不过,系统Sun的G1实现以后,在这方面会有一个质的飞跃。
    

    Garbage Firest(G1)

    目标

    从设计目标看G1完全是为了大型应用而准备的。

    支持很大的堆

    高吞吐量

    –支持多CPU和垃圾回收线程

    –在主线程暂停的情况下,使用并行收集

    –在主线程运行的情况下,使用并发收集

    实时目标:可配置在N毫秒内最多只占用M毫秒的时间进行垃圾回收

    并发的关键是你有处理多个任务的能力,不一定要同时。 并行的关键是你有同时处理多个任务的能力。 并行算法是用多线程进行垃圾回收,回收期间会暂停程序的执行,而并发算法,也是多线程回收,但期间不停止应用执行。所以,并发算法适用于交互性高的一些程序。经过观察,并发算法会减少年轻代的大小,其实就是使用了一个大的年老代,这反过来跟并行算法相比吞吐量相对较低。

    G1可谓博采众家之长,力求到达一种完美。他吸取了增量收集优点,把整个堆划分为一个一个等大小的区域(region)。内存的回收和划分都以region为单位;同时,他也吸取了CMS的特点,把这个垃圾回收过程分为几个阶段,分散一个垃圾回收过程;而且,G1也认同分代垃圾回收的思想,认为不同对象的生命周期不同,可以采取不同收集方式,因此,它也支持分代的垃圾回收。为了达到对回收时间的可预计性,G1在扫描了region以后,对其中的活跃对象的大小进行排序,首先会收集那些活跃对象小的region,以便快速回收空间(要复制的活跃对象少了),因为活跃对象小,里面可以认为多数都是垃圾,所以这种方式被称为Garbage First(G1)的垃圾回收算法,即:垃圾优先的回收。

    回收步骤:

    初始标记(Initial Marking)

    G1对于每个region都保存了两个标识用的bitmap,一个为previous marking bitmap,一个为next marking bitmap,bitmap中包含了一个bit的地址信息来指向对象的起始点。
    
    开始Initial Marking之前,首先并发的清空next marking bitmap,然后停止所有应用线程,并扫描标识出每个region中root可直接访问到的对象,将region中top的值放入next top at mark start(TAMS)中,之后恢复所有应用线程。
    
    触发这个步骤执行的条件为:
    
    G1定义了一个JVM Heap大小的百分比的阀值,称为h,另外还有一个H,H的值为(1-h)*Heap Size,目前这个h的值是固定的,后续G1也许会将其改为动态的,根据jvm的运行情况来动态的调整,在分代方式下,G1还定义了一个u以及soft limit,soft limit的值为H-u*Heap Size,当Heap中使用的内存超过了soft limit值时,就会在一次clean up执行完毕后在应用允许的GC暂停时间范围内尽快的执行此步骤;
    
    在pure方式下,G1将marking与clean up组成一个环,以便clean up能充分的使用marking的信息,当clean up开始回收时,首先回收能够带来最多内存空间的regions,当经过多次的clean up,回收到没多少空间的regions时,G1重新初始化一个新的marking与clean up构成的环。
    

    并发标记(Concurrent Marking)

    按照之前Initial Marking扫描到的对象进行遍历,以识别这些对象的下层对象的活跃状态,对于在此期间应用线程并发修改的对象的以来关系则记录到remembered set logs中,新创建的对象则放入比top值更高的地址区间中,这些新创建的对象默认状态即为活跃的,同时修改top值。
    

    最终标记暂停(Final Marking Pause)

    当应用线程的remembered set logs未满时,是不会放入filled RS buffers中的,在这样的情况下,这些remebered set logs中记录的card的修改就会被更新了,因此需要这一步,这一步要做的就是把应用线程中存在的remembered set logs的内容进行处理,并相应的修改remembered sets,这一步需要暂停应用,并行的运行。
    

    存活对象计算及清除(Live Data Counting and Cleanup)

    值得注意的是,在G1中,并不是说Final Marking Pause执行完了,就肯定执行Cleanup这步的,由于这步需要暂停应用,G1为了能够达到准实时的要求,需要根据用户指定的最大的GC造成的暂停时间来合理的规划什么时候执行Cleanup,另外还有几种情况也是会触发这个步骤的执行的:
    
    G1采用的是复制方法来进行收集,必须保证每次的”to space”的空间都是够的,因此G1采取的策略是当已经使用的内存空间达到了H时,就执行Cleanup这个步骤;
    
    对于full-young和partially-young的分代模式的G1而言,则还有情况会触发Cleanup的执行,full-young模式下,G1根据应用可接受的暂停时间、回收young regions需要消耗的时间来估算出一个yound regions的数量值,当JVM中分配对象的young regions的数量达到此值时,Cleanup就会执行;partially-young模式下,则会尽量频繁的在应用可接受的暂停时间范围内执行Cleanup,并最大限度的去执行non-young regions的Cleanup。
    

    经典配置举例

    以下配置主要针对分代垃圾回收算法而言。

    堆大小设置

    年轻代的设置很关键

    JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。

    典型设置:

    java -Xmx3550m -Xms3550m -Xmn2g –Xss128k 为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值

    -Xmx3550m:设置JVM最大可用内存为3550M。

    -Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

    -Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

    -Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

    java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

    -XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 默认的比例(1:2)

    -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

    -XX:MaxPermSize=16m:设置持久代大小为16m。

    -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

    回收器选择

    JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

    吞吐量优先的并行收集器

    如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

    典型配置:

    java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

    -XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。

    -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

    -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

    -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

    n java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

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

    响应时间优先的并发收集器

    如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

    典型配置:

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

    -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。

    -XX:+UseParNewGC: 设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

    -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

    -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

    转自 JVM调优总结(七)-典型配置举例1

    可以通过下面的参数打Heap Dump信息

    -XX:HeapDumpPath
    -XX:+PrintGCDetails
    -XX:+PrintGCTimeStamps
    -Xloggc:/usr/aaa/dump/heap_trace.txt
    通过下面参数可以控制OutOfMemoryError时打印堆的信息

    -XX:+HeapDumpOnOutOfMemoryError
    请看一下一个时间的Java参数配置:(服务器:Linux 64Bit,8Core×16G)

    JAVA_OPTS="$JAVA_OPTS -server -Xms3G -Xmx3G -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/aaa/dump -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt -XX:NewSize=1G -XX:MaxNewSize=1G"

    经过观察该配置非常稳定,每次普通GC的时间在10ms左右,Full GC基本不发生,或隔很长很长的时间才发生一次

    通过分析dump文件可以发现,每个1小时都会发生一次Full GC,经过多方求证,只要在JVM中开启了JMX服务,JMX将会1小时执行一次Full GC以清除引用,关于这点请参考附件文档。

    调优实例分析

    实例1:

    发现部分开发测试机器出现异常:java.lang.OutOfMemoryError: GC overhead limit exceeded,这个异常代表:

    GC为了释放很小的空间却耗费了太多的时间,其原因一般有两个:1,堆太小,2,有死循环或大对象;

    首先排除了第2个原因,因为这个应用同时是在线上运行的,如果有问题,早就挂了。所以怀疑是这台机器中堆设置太小;

    使用ps -ef |grep "java"查看,发现:
    在这里插入图片描述
    该应用的堆区设置只有768m,而机器内存有2g,机器上只跑这一个java应用,没有其他需要占用内存的地方。另外,这个应用比较大,需要占用的内存也比较多;

    通过上面的情况判断,只需要改变堆中各区域的大小设置即可,于是改成下面的情况:
    在这里插入图片描述
    跟踪运行情况发现,相关异常没有再出现;

    实例2:

    一个服务系统,经常出现卡顿,分析原因,发现Full GC时间太长:

    jstat -gcutil:

    S0 S1 E O P YGC YGCT FGC FGCT GCT

    12.16 0.00 5.18 63.78 20.32 54 2.047 5 6.946 8.993

    分析上面的数据,发现Young GC执行了54次,耗时2.047秒,每次Young GC耗时37ms,在正常范围,而Full GC执行了5次,耗时6.946秒,每次平均1.389s,数据显示出来的问题是:Full GC耗时较长,分析该系统的是指发现,NewRatio=9,也就是说,新生代和老生代大小之比为1:9,这就是问题的原因:

    1,新生代太小,导致对象提前进入老年代,触发老年代发生Full GC;

    2,老年代较大,进行Full GC时耗时较大;

    优化的方法是调整NewRatio的值,调整到4,发现Full GC没有再发生,只有Young GC在执行。这就是把对象控制在新生代就清理掉,没有进入老年代(这种做法对一些应用是很有用的,但并不是对所有应用都要这么做)

    实例3:

    一应用在性能测试过程中,发现内存占用率很高,Full GC频繁,使用sudo -u admin -H jmap -dump:format=b,file=文件名.hprof pid 来dump内存,生成dump文件,并使用Eclipse下的mat差距进行分析,发现:
    在这里插入图片描述
    从图中可以看出,这个线程存在问题,队列LinkedBlockingQueue所引用的大量对象并未释放,导致整个线程占用内存高达378m,此时通知开发人员进行代码优化,将相关对象释放掉即可。

    展开全文
  • JVM调优经验分享

    万次阅读 多人点赞 2016-03-30 12:06:47
    JVM调优经验分享 前言 一、JVM调优知识背景简介 二、JVM调优参数简介 三、JVM调优目标 四、JVM调优经验 结束语
  • JVM调优

    2021-06-20 20:28:41
    JVM调优 1、目的 根据需求进行JVM规划预调优 减少full gc次数,减少full gc时间 2、ERP调优步骤 -XX:+PrintGCDetails 输出GC的详细日志 调整元空间大小 -XX:MetaspaceSize -XX:MaxMetaspaceSize 如何发现垃圾? ...
  • JVM调优总结

    2021-07-12 18:38:55
    文章目录调优理解JVM调优的时机JVM调优的目标JVM调优步骤JVM参数基本参数Jdk7版本的主要参数Jdk8版本的重要特有参数并行收集器相关参数CMS相关参数辅助信息主要工具JDK工具Linux 命令行工具常用调优策略选择合适的...
  • Jvm调优

    2019-08-20 00:51:33
    Jvm调优概念 Jvm调优目的 对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数,过多的GC和Full GC是会占用很多的系统资源(主要是CPU),影响系统的吞吐量。特别要关注Full GC,因为它会对整个堆进行...
  • JVM调优6大步骤

    2019-04-15 18:11:00
    直通BAT必考题系列:JVM性能调优的6大步骤,及关键调优参数详 JVM系列 直通BAT必考题系列:7种JVM垃圾收集器特点,优劣势、及使用场景 直通BAT必考题系列:JVM的4种垃圾回收算法、垃圾回收机制与总结 直通BAT必...
  • JVM调优6大步骤 直通BAT必考题系列:JVM性能调优的6大步骤,及关键调优参数详 JVM系列 直通BAT必考题系列:7种JVM垃圾收集器特点,优劣势、及使用场景 直通BAT必考题系列:JVM的4种垃圾回收算法、垃圾回收机制与...
  • JVM调优概述

    2021-02-05 14:04:03
    JVM调优概述 1. 大厂面试题 如何进行JVM调优?有哪些方法? 如何理解内存泄漏问题?有哪些情况会导致内存泄漏?如何解决? 字节跳动: 三面:JVM如何调优、参数怎么调? 拼多多: 从SQL、JVM、架构、数据库四个方面...
  • 文章目录一、性能调优的层次二、jvm调优流程1、性能定义2、性能调优原则3、性能调优流程三、确定内存占用1、运行阶段2、jvm内存分配&参数3、计算活跃数据大小四、延迟调优1、系统延迟需求2、优化新生代的大小3、...
  • jvm调优

    2020-08-27 19:22:52
    jvm调优 2.1 优化目标 优化jvm其实是为了满足高吞吐,低延迟需求来优化GC,之前遇到的情况中,其实不优化GC也是可以正常运行的,只不过偶尔会因为高并发给压垮,但是也可以通过其他方式来解决这个问题。 2.2 优化...
  • jvm调优的基本步骤

    2021-03-17 16:35:58
    quit:退出arthas客户端 dashboard:当前进程的实时数据面板 thread:当前JVM的线程堆栈信息 jvm:查看当前JVM的信息 sysprop:查看JVM的系统属性 sc:查看JVM已经加载的类信息 dump:dump已经加载类的byte code到特定目录 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,335
精华内容 7,334
关键字:

jvm调优步骤