花乡做网站公司,建筑网站知乎,自己做的网站有什么用,网站开发工作好吗理论篇 1多功能养鱼塘#xff0d;JVM内存
大鱼塘O#xff08;可分配内存#xff09;#xff1a; JVM可以调度使用的总的内存数#xff0c;这个数量受操作系统进程寻址范围、系统虚拟内存总数、系统物理内存总数、其他系统运行所占用的内存资源等因素的制约。
小池塘AJVM内存
大鱼塘O可分配内存 JVM可以调度使用的总的内存数这个数量受操作系统进程寻址范围、系统虚拟内存总数、系统物理内存总数、其他系统运行所占用的内存资源等因素的制约。
小池塘A堆内存JVM运行时数据区域它为类实例和数组分配的内存。堆可以是固定大小的也可以是可变大小的。其中 Heap {Old NEW { Eden , from, to } }。
小池塘B非堆内存包括所有线程之间共享的一个方法区域和JVM为优化或内部处理所分配的内存。它存储每一个类的结构如一个运行时的常量池、字段和方法数据、方法的代码和构造函数。这个方法区是逻辑上堆的一部分但依赖于实现一个JVM可以不去回收或者压缩它。像堆一样方法区可以固定大小的也可以是大小可变的。方法区不是必须是连续的它们可以是不连续的。除方法区之外JVM总是从非堆中分配用于优化和内部处理所需的内存。例如JIT编译器为高性能的JVM代码转换存储成本地代码而分配的内存。
整个池塘结构图如下 查看大池塘O大小的方法为
在命令行下用 java -XmxXXXXM -version 命令来进行测试然后逐渐的增大XXXX的值如果执行正常就表示指定的内存大小可用否则会打印错误信息示例如下
java -Xmx3072M -version。
当一个URL被访问时内存申请过程如下
A. JVM会试图为相关Java对象在Eden中初始化一块内存区域
B. 当Eden空间足够时内存申请结束。否则到下一步
C. JVM试图释放在Eden中所有不活跃的对象这属于1或更高级的垃圾回收, 释放后若Eden空间仍然不足以放入新对象则试图将部分Eden中活跃对象放入Survivor区
D. Survivor区被用来作为Eden及OLD的中间交换区域当OLD区空间足够时Survivor区的对象会被移到Old区否则会被保留在Survivor区
E. 当OLD区空间不够时JVM会在OLD区进行完全的垃圾收集0级
F. 完全垃圾收集后若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象导致JVM无法在Eden区为新对象创建内存区域则出现”out of memory错误” 2池塘中的鱼程序中的对象
程序中运行的各种类实例称之为对象每个对象都有不同的生命周期有的存活时间长点有的存活时间短点这就想鱼塘中养的不同生长期的鱼一样有的三个月就可以上市有的鱼则需要6个月甚至更长的时间才能上市。JVM内存机制的设置就是为了要满足这种不同生命周期的对象对内存的需求并使之能达到最大的性能表现。 3养殖区域划分JVM中的代
鱼塘主人为了充分利用现有的条件来赚取更多的利润他需要喂养各种不同种类的鱼于是又把鱼塘分割成了几块不同区域“鱼苗养殖区”、“短中期养殖区”、“长期养殖区”来养殖不同生长周期的鱼。JVM同样为了对各种不同生命周期的对象进行有效管理也划分了各种不同的区域这就是“代”的概念分别叫做“青年代”、“老年代”、“持久代”下面逐一介绍每个代的含义和作用。
短中期鱼苗养殖区年青代(Young Generation)
年青代由一个Eden Space和两个Survivor Spaces组成虚拟机初始时分配所有的对象到Eden Space许多对象也是在这里死去。当它执行一个“minor GC”的时候虚拟机将从Eden Space中移动一些残余的对象到其中的一个Survivor Spaces中。青年代就好像养鱼塘中的“中短期养殖区”一样主人把鱼先投放到“短期养殖区”喂养隔一段时间就开始下网捞出已经长成的那些鱼拿到集市去卖这个过程就是从“Eden Space”中执行垃圾回收的过程。主人接着把捕捞之后剩下的“漏网之鱼”赶到“中期养殖区”继续喂养。这个“中期养殖区”就是“Survivor Spaces”当然鱼在“中期养殖区”喂养一段时间后也要捞出那些长成的鱼去卖这就是对“Survivor Spaces” 执行垃圾回收的过程。
Ps Eden Space: 这个内存池在对象初始化时被分配
Ps Survivor Space: 这个内存池中包含着Eden Space 经过GC之后幸存下来的对象
年轻代设置策略对于响应时间优先的应用需尽可能设大直到接近系统的最低响应时间限制根据实际情况选择。在此种情况下年轻代收集发生的频率也是最小的。同时减少到达老年代的对象。对于吞吐量优先的应用则尽可能的设置大可达到Gbit的程度。因为对响应时间没有要求垃圾收集可以并行进行一般适合8CPU以上的应用。
长期养殖区老年代(老年代)、
虚拟机将在Survivor Spaces中生存足够长时间的对象移动到老年代的Tenured Spaces中。当Tenured Generation被填满则将执行一个完全GC这个完全GC非常的慢因为它要处理所有存活着的对象用的是串行标记收集的方式并发收集可以减少对于应用的影响。
老年代设置策略对于响应时间优先的应用老年代使用并发收集器所以其大小需要小心设置一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了可能会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式如果堆大了则需要较长的收集时间。
最优化的方案一般需要参考以下数据获得
并发垃圾收集信息持久代并发收集次数传统GC信息花在年轻代和老年代回收上的时间比例减少年轻代和老年代花费的时间一般会提高应用的效率
对于吞吐量优先的应用一般吞吐量优先的应用都有一个很大的年轻代和一个较小的老年代。原因是这样可以尽可能回收掉大部分短期对象减少中期的对象而老年代尽存放长期存活对象。
较小堆引起的碎片问题 因为老年代的并发收集器使用标记、清除算法所以不会对堆进行压缩。当收集器回收时他会把相邻的空间进行合并这样可以分配给较大的对象。但是当堆空间较小时运行一段时间以后就会出现“碎片”如果并发收集器找不到足够的空间那么并发收集器将会停止然后使用传统的标记、清除方式进行回收。如果出现“碎片”可能需要进行如下配置 -XX:UseCMSCompactAtFullCollection 使用并发收集器时开启对老年代的压缩-XX:CMSFullGCsBeforeCompaction0上面配置开启的情况下这里设置多少次Full GC后对老年代进行压缩。
/*监控实例*/
内存池名称: Tenured Gen
Java 虚拟机最初向操作系统请求的内存量: 3,538,944 字节
Java 虚拟机实际能从操作系统获得的内存量: 1,431,699,456 字节
Java 虚拟机可从操作系统获得的最大内存量: 1,431,699,456 字节。请注意并不一定能获得该内存量。
Java 虚拟机此时使用的内存量: 1,408,650,472 字节
/*监控实例*/
实例说明系统能获得的最大Tenured Generation空间大小为1.431G左右此时使用已经1.408G基本满了所以在JVM执行串行标记垃圾收集时系统响应速度会很慢
鱼苗养殖区持久代Permanent Generation
控制着所有虚拟机自己映射的数据如类和对象的方法。在持久代中jvm则存储class和method对象。持久代就像鱼苗养殖区一样池塘主人一次对该区域投入足够量的鱼苗已保证其他鱼塘的足够供应。就配置而言永久域是一个独立域并且不认为是堆的一部分。永久域默认大小为4m.运行程序时jvm会调整永久域的大小以满足需要。每次调整时jvm会对堆进行一次完全的垃圾收集。 使用-XXMaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时经常需要增加永久域的最大值。当jvm加载类时永久域中的对象急剧增加从而使jvm不断调整永久域大小。为了避免调整可使用-XXPerSize标志设置初始值。 4主人定期捕鱼JVM垃圾回收
一个池塘收容积限制能养殖的鱼的数量是一定的因此隔一段时间必须捞出部分长成的鱼来使主人能喂养很多的鱼。同样JVM所管理的有限内存也要实现最优化利用Garbage CollectionGC就是用来释放没有被引用的对象所占领的内存,目的在于清除不再使用的对象。GC通过算法和参数的配置可以对性能产生效果显著的影响。
GC就好像把长成的鱼从池塘中捞出来拿到市场上去卖然后给池塘腾出空间继续养别的鱼赚钱。采用哪种养殖方式能让鱼塘主人赚到更大的利润是鱼塘主人的经营目的而JVM调优的目的在于如何能是系统表现出更好的响应时间、更大的吞吐量。
Minor Collections局部垃圾回收当通用内存消耗完被分配的内存时JVM会在内存池上执行一个局部的GC总是调用minor collection去释放被dead的对象所占用的内存。这个局部的GC通常比完全GC要快许多。青年代中的垃圾回收就是采用局部垃圾回收机制因此青年代中内存分配和管理效率也是最高。
通常情况下对于内存的申请优先在青年代中申请当内存不够时会整理新生代当整理以后还是不能满足申请的内存就会向老年代移动一些生命周期较长的对象。这种整理和移动会消耗资源同时降低系统运行响应能力因此如果青年代设置的过小就会频繁的整理和移动对性能造成影响。那是否把年青代设置的越大越好其实不然青年代采用的是复制搜集算法这种算法必须停止所有应用程序线程服务器线程切换时间就会成为应用响应的瓶颈。
Major Collections完全垃圾回收当老年代需要被回收这就是一个major collection 它的运行常常非常慢因为它要涉及所有存活着的类。
/*实例*/
垃圾收集器的名称: Copy
使用此垃圾收集器收集的数量: 219 字节
垃圾收集时间: 18 秒 630 毫秒
垃圾收集器的名称: MarkSweepCompact
使用此垃圾收集器收集的数量: 47 字节
垃圾收集时间: 36 秒 166 毫秒
实例说明copy垃圾搜集器的运行时间为18秒回收219字节回收速度为平均每秒12字节而MKC垃圾搜集器的时间为36秒回收了47字节回收速度为平均每秒1.3字节两者差距几乎达到了10倍可见完全垃圾回收的速度远不如局部垃圾回收。 5不同的捕鱼方式垃圾回收器
Sun JVM提供有4垃圾回收器
Serial Collector序列垃圾回收器垃圾回收器对Young Gen和Tenured Gen都是使用单线的垃圾回收方式对Young Gen会使用拷贝策略避免内存碎片对Old Gen会使用压缩策略避免内存碎片。在JVM启动参数中使用-XX:UseSerialGC启用Serial Collector。串行收集器只适用于小数据量的情况默认情况下JDK5.0以前都是使用串行收集器如果想使用其他收集器需要在启动时加入相应参数。基本上在多内核的服务器上应该避免使用这种方式。JDK5.0以后JVM会根据当前系统配置进行判断。串行GC适合小型应用和单处理器系统无需多线程交互效率比较高。
Parallel Collector并发垃圾回收器垃圾回收器对Young Gen和Tenured Gen都是使用多线程并行垃圾回收的方式对Young Gen会使用拷贝策略避免内存碎片对Old Gen会使用压缩策略避免内存碎片。在JVM启动参数中使用-XX:UseParallelGC启用Parallel Collector。这是一种吞吐量优先的并行收集器 主要以到达一定的吞吐量为目标适用于科学技术和后台处理等。采用了多线程并行管理和回收垃圾对象提高了回收效率和服务器的吞吐量适合于多处理器的服务器。
Parallel Compacting Collector并行压缩垃圾回收器)与Parallel Collector垃圾回收类似但对Tenured Gen会使用一种更有效的垃圾回收策略此垃圾回收器在暂停时间上会更短。在JVM启动参数中使用-XX:UseParallelOldGC启用Parallel Compacting Collector。这是一种响应时间优先的并发收集器 主要是保证系统的响应时间减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
Concurrent Mark-Sweep (CMS) Collector并发标志清除垃圾回收器)对Young Gen会使用与Parallel Collector同样的垃圾回收策略对Tenured Gen垃圾回收的垃圾标志线程与应用线程同时进行而垃圾清除则需要暂停应用线程但暂停时间会大大缩减需要注意的是由于垃圾回收过程更加复杂会降低总体的吞吐量。
这里说一下并行和并发的区别并行指的是多个进程并行执行垃圾回收那么可以很好的利用多处理器而并发指的是应用程序不需要暂停可以和垃圾回收线程并发工作。
说明对于联机处理的应用系统或复杂的3层应用系统采用Concurrent Mark-Sweep (CMS) Collector进行垃圾搜集基本上既能保证性能又能保证稳定性暂停时间短。 5捕鱼工具选择JVM参数 (1) 通用JVM参数
-server
如果不配置该参数JVM会根据应用服务器硬件配置自动选择不同模式server模式启动比较慢但是运行期速度得到了优化适合于服务器端运行的JVM。
-client
启动比较快但是运行期响应没有server模式的优化适合于个人PC的服务开发和测试。
-Xmx
设置java heap的最大值默认是机器物理内存的1/4。这个值决定了最多可用的Java堆内存分配过少就会在应用中需要大量内存作缓存或者临时对象时出现OOMOut Of Memory的问题如果分配过大那么就会因PermSize过小而引起的另外一种Out Of Memory。所以如何配置还是根据运行过程中的分析和计算来确定如果不能确定还是采用默认的配置。
-Xms
设置Java堆初始化时的大小默认情况是机器物理内存的1/64。这个主要是根据应用启动时消耗的资源决定分配少了申请起来会降低运行速度分配多了也浪费。
-XX:PermSize
初始化永久内存区域大小。永久内存区域全称是Permanent Generation space是指内存的永久保存区域程序运行期不对PermGen space进行清理所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。 如果你的WEB APP下用了大量的第三方jar其大小超过了jvm默认的PermSize大小(4M)那么就会产生此错误信息了。
-XX:MaxPermSize
设置永久内存区域最大大小。
-Xmn
直接设置青年代大小。整个JVM可用内存大小青年代大小 老年代大小 持久代大小 。持久代一般固定大小为64m所以增大年轻代后将会减小老年代大小。此值对系统性能影响较大Sun官方推荐配置为整个堆的3/8。
按照Sun的官方设置比例则上面的例子中年轻代的大小应该为2048*3/8768M。
-XX:NewRatio
控制默认的Young代的大小例如设置-XX:NewRatio3意味着Young代和老年代的比率是1:3。换句话说Eden和Survivor空间总和是整个堆大小的1/4。 如图中的实际设置-XX:NewRatio2-Xmx2048则年轻代和老年代的分配比例为1:2即年轻代的大小为682M而老年代的大小为1365M。查看实际系统的jvm监控结果为
内存池名称: Tenured Gen
Java 虚拟机最初向操作系统请求的内存量: 3,538,944 字节
Java 虚拟机实际能从操作系统获得的内存量: 1,431,699,456 字节
Java 虚拟机可从操作系统获得的最大内存量: 1,431,699,456 字节。请注意并不一定能获得该内存量。
Java 虚拟机此时使用的内存量: 1,408,650,472 字节
即1,408,650,472 字节1365M证明了上面的计算是正确的。
-XX:SurvivorRatio
设置年轻代中Eden区与Survivor区的大小比值。设置为4则两个Survivor区与一个Eden区的比值为2:4一个Survivor区占整个年轻代的1/6。越大的survivor空间可以允许短期对象尽量在年青代消亡如果Survivor空间太小Copying收集将直接将其转移到老年代中这将加快老年代的空间使用速度引发频繁的完全垃圾回收。
如下图 SurvivorRatio的值设为3Xmn为768M则每个Survivor空间的大小为768M/5153.6M。
-XX:NewSize
为了实现更好的性能您应该对包含短期存活对象的池的大小进行设置以使该池中的对象的存活时间不会超过一个垃圾回收循环。新生成的池的大小由 NewSize 和 MaxNewSize 参数确定。通过这个选项可以设置Java新对象生产堆内存。在通常情况下这个选项的数值为1024的整数倍并且大于1MB。这个值的取值规则为一般情况下这个值-XX:NewSize是最大堆内存maximum heap size的四分之一。增加这个选项值的大小是为了增大较大数量的短生命周期对象。增加Java新对象生产堆内存相当于增加了处理器的数目。并且可以并行地分配内存但是请注意内存的垃圾回收却是不可以并行处理的。作用跟-XX:NewRatio相似 -XX:NewRatio是设置比例而-XX:NewSize是设置精确的数值。
-XX:MaxNewSize
通过这个选项可以设置最大Java新对象生产堆内存。通常情况下这个选项的数值为1 024的整数倍并且大于1MB其功用与上面的设置新对象生产堆内存-XX:NewSize相同。一般要将NewSize和MaxNewSize设成一致。
-XX:MaxTenuringThreshold
设置垃圾最大年龄。如果设置为0的话则年轻代对象不经过Survivor区直接进入老年代。对于老年代比较多的应用可以提高效率。如果将此值设置为一个较大值则年轻代对象会在Survivor区进行多次复制这样可以增加对象在年轻代的存活时间增加在年轻代即被回收的概率。
如下图 -XX:MaxTenuringThreshold参数被设置成5表示对象会在Survivor区进行5次复制后如果还没有被回收才会被复制到老年代。
-XX:GCTimeRatio
设置垃圾回收时间占程序运行时间的百分比。该参数设置为n的话则垃圾回收时间占程序运行时间百分比的公式为1/(1n) 如果n19表示java可以用5%的时间来做垃圾回收1/(119)1/205%。
-XX:TargetsurvivorRatio
该值是一个百分比控制允许使用的救助空间的比例默认值是50。该参数设置较大的话可提高对survivor空间的使用率。当较大的堆栈使用较低的SurvivorRatio时应增加该值到80至90以更好利用救助空间。
-Xss
设置每个线程的堆栈大小根据应用的线程所需内存大小进行调整在相同物理内存下减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的不能无限生成经验值在3000~5000左右。当这个选项被设置的较大2MB时将会在很大程度上降低系统的性能。因此在设置这个值时应该格外小心调整后要注意观察系统的性能不断调整以期达到最优。
JDK5.0以后每个线程堆栈大小为1M以前每个线程堆栈大小为256K。
-Xnoclassgc
这个选项用来取消系统对特定类的垃圾回收。它可以防止当这个类的所有引用丢失之后这个类仍被引用时不会再一次被重新装载因此这个选项将增大系统堆内存的空间。禁用类垃圾回收性能会高一点
(2)串行收集器参数
-XX:UseSerialGC:
设置串行收集器 。
(3)并行收集器参数
-XX:UseParallelGC:
选择垃圾收集器为并行收集器此配置仅对年轻代有效即上述配置下年轻代使用并行收集而老年代仍旧使用串行收集。采用了多线程并行管理和回收垃圾对象提高了回收效率提高了服务器的吞吐量适合于多处理器的服务器。
-XX:ParallelGCThreads
配置并行收集器的线程数即同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
-XX:UseParallelOldGC
采用对于老年代并发收集的策略可以提高收集效率。JDK6.0支持对老年代并行收集。
-XX:MaxGCPauseMillis
设置每次年轻代并行收集最大暂停时间如果无法满足此时间JVM会自动调整年轻代大小以满足此值。
-XX:UseAdaptiveSizePolicy
设置此选项后并行收集器会自动选择年轻代区大小和相应的Survivor区比例以达到目标系统规定的最低响应时间或者收集频率等此值建议使用并行收集器时一直打开。
(4)并发收集器参数
-XX:UseConcMarkSweepGC
指定在 老年代 使用 concurrent cmark sweep gc。gc thread 和 app thread 并行 ( 在 init-mark 和 remark 时 pause app thread)。app pause 时间较短 , 适合交互性强的系统 , 如 web server。它可以并发执行收集操作降低应用停止时间同时它也是并行处理模式可以有效地利用多处理器的系统的多进程处理。
-XX:UseParNewGC
指定在 New Generation 使用 parallel collector, 是 UseParallelGC 的 gc 的升级版本 , 有更好的性能或者优点 , 可以和 CMS gc 一起使用
-XX:UseCMSCompactAtFullCollection
打开对老年代的压缩。可能会影响性能但是可以消除碎片,在FULL GC的时候 压缩内存 CMS是不会移动内存的 因此 这个非常容易产生碎片 导致内存不够用 因此 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。
-XX:CMSIncrementalMode:
设置为增量模式。适用于单CPU情况
-XX:CMSFullGCsBeforeCompaction
由于并发收集器不对内存空间进行压缩、整理所以运行一段时间以后会产生“碎片”使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:CMSClassUnloadingEnabled
使CMS收集持久代的类而不是fullgc
-XX:CMSPermGenSweepingEnabled
使CMS收集持久代的类而不是fullgc。
-XX:-CMSParallelRemarkEnabled
在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间。
-XX:CMSInitiatingOccupancyFraction
说明老年代到百分之多少满的时候开始执行对老年代的并发垃圾回收CMS这个参数设置有很大技巧基本上满足公式
(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100Xmn
时就不会出现promotion failed。在我的应用中Xmx是6000Xmn是500那么Xmx-Xmn是5500兆也就是老年代有5500兆CMSInitiatingOccupancyFraction90说明老年代到90%满的时候开始执行对老年代的并发垃圾回收CMS这时还剩10%的空间是5500*10%550兆所以即使Xmn也就是年轻代共500兆里所有对象都搬到老年代里550兆的空间也足够了所以只要满足上面的公式就不会出现垃圾回收时的promotion failed
如果按照Xmx2048,Xmn768的比例计算则CMSInitiatingOccupancyFraction的值不能超过40否则就容易出现垃圾回收时的promotion failed。
-XX:UseCMSInitiatingOccupancyOnly
指示只有在老年代在使用了初始化的比例后 concurrent collector 启动收集
-XX:SoftRefLRUPolicyMSPerMB
相对于客户端模式的虚拟机-client选项当使用服务器模式的虚拟机时-server选项对于软引用soft reference的清理力度要稍微差一些。可以通过增大-XX:SoftRefLRUPolicyMSPerMB来降低收集频率。默认值是 1000也就是说每秒一兆字节。Soft reference在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数 -XX:SoftRefLRUPolicyMSPerMBN 来控制这可以指定每兆堆空闲空间的 soft reference 保持存活一旦它不强可达了的毫秒数这意味着每兆堆中的空闲空间中的 soft reference 会在最后一个强引用被回收之后存活1秒钟。注意这是一个近似的值因为 soft reference 只会在垃圾回收时才会被清除而垃圾回收并不总在发生。
-XX:LargePageSizeInBytes
内存页的大小 不可设置过大会影响Perm的大小。
-XX:UseFastAccessorMethods
原始类型的快速优化get,set 方法转成本地代码。
-XX:DisableExplicitGC
禁止 java 程序中的 full gc, 如 System.gc() 的调用。 最好加上防止程序在代码里误用了对性能造成冲击。
-XX:AggressiveHeap
特别说明下(我感觉对于做java cache应用有帮助)
试图是使用大量的物理内存
长时间大内存使用的优化能检查计算资源内存 处理器数量
至少需要256MB内存
大量的CPU内存 在1.4.1在4CPU的机器上已经显示有提升
-XX:AggressiveOpts
加快编译
-XX:UseBiasedLocking
锁机制的性能改善。 6、实战篇 1测试目的
测试被测系统使用不同的垃圾回收方案时的性能表现
了解各种JVM参数在性能调优时的实际效果
对遴选出的最优方案进行8小时压力测试并记录测试结果
2测试环境准备
被测程序的运行的软硬件环境
D630 4G内存T7250双核CPU160G硬盘操作系统windowsXP SP3IP11.55.15.51
被测程序名称
XXX银行采购管理系统V1.1版
程序部署环境
Tomcat6.0.18 for windowsSun JDK1.6.13 for windowsOracle10g for windows单独运行在另外一台640M笔记本上
性能测试工具运行的软硬件环境
操作系统windowsxp sp3浏览器版本IE7IP地址11.55.15.141性能测试工具loadrunner9.10
JVM监控工具
3使用Jconsole进行图形化监控
确定JVM在被测系统的机器上最大可用内存
通过在命令行下用 java -XmxXXXXM -version 命令反复测试发现在11.55.15.51机器上JVM能使用的最大内存为1592M。 3录制测试脚本
录制前准备修改checkcode.java文件将随机生成的校验码改成一个固定的校验码方便脚本的自动运行然后将编译好的checkcode.class文件替换发布包中的class文件。
选择典型业务操作进行脚本录制每个系统的典型业务操作都会不同需要经过分析统计选择用户操作频率最高的部分。经过分析后确定的脚本内容为录制系统登录操作并在登录成功后的主界面上选取一段文字作为验证点。
启动VuGen程序按脚本定义内容进行录制并调试脚本保证脚本能正常运行。
4定义测试场景
虚拟用户数30持续运行时间8小时虚拟用户加载和卸载方式同时性能监控指标响应时间、吞吐量、成功交易数 7、执行初步性能测试
使用系统默认的参数执行测试并记录响应时间、吞吐量已经成功交易数等数据同时监控JVM的使用情况。 7.1选择调优方案
不同垃圾回收方法测试数据 Id NewRatio SurviorRatio TransResponse Time Throughput Passed Transactions 1 2 25 3.139s 3016230.514 7528 2 1 25 3.161s 2975581.301 7452 3 3 25 2.814s 3334717.818 8383 4 4 25 2.659s 3505592.450 8846 5 5 25 2.860s 3270596.069 8232 6 4 15 2.499s 3765121.986 9426 7 4 5 1.986s 4750776.581 11843 8 4 4 1.968s 4825608.161 11947 9 4 3 2.507s 3770420.243 9388 10 -XX:TargetSurvivorRatio90 1.924 4945053.874 12216 11 -Xmx1024M 1.903 4974137.908 12360
并发收集模式运行时间十分钟后的对内存使用情况 串行收集模式运行时间十分钟 并行收集模式运行时间十分钟 30-60:30个并发用户连续运行60分钟的jvm内存变化截图 在11:36和11:56分发生了两次完全GCFull GC因为这时PS Old Gen已经满了JVM自动对Old Gen中的内存进行了回收。
根据反复的测试并结合被测系统业务特点最终敲定了使用以下最优方案进行8小时压力测试
JAVA_OPTS-server
-Xms1024M
-Xmx1024M
-Xmn128M
-XX:NewSize128M
-XX:MaxNewSize128M
-XX:SurvivorRatio20
-XX:MaxTenuringThreshold10
-XX:GCTimeRatio19
-XX:UseParNewGC
-XX:UseConcMarkSweepGC
-XX:CMSClassUnloadingEnabled
-XX:UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction0
-XX:-CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction70
-XX:SoftRefLRUPolicyMSPerMB0
–XX:PermSize256m
-XX:MaxPermSize256m
-Djava.awt.headlesstrue
7.2调优后JVM监控图
30Vusers运行8小时截图 8、测试结果分析
对于XX银行采购系统的登录操作来说将jvm的NewRatio 和SurviorRatio设置成4时性能表现最好在此基础上在设置-XX:TargetSurvivorRatio90和-Xmx1024M后性能也有一定程度的提升。 8.1性能问题举例 8.1.1性能症状
XX省一个正式上线运行的系统每运行一段时间后程序进程会莫名其妙地被kill掉不得不手工启动系统。 8.1.2监控结果 8.1.2.1jmap命令查看堆内存分配和使用情况
./jmap -heap 31 //31为程序的进程号
Attaching to process ID 31, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0-b12 //显示jvm的版本号
using parallel threads in the new generation. //说明在年轻代使用了并行收集
using thread-local object allocation.
Concurrent Mark-Sweep GC //启用CMS收集模式 Heap Configuration: MinHeapFreeRatio 40 MaxHeapFreeRatio 70 //这两项说明堆内存的使用比例在30%60%之间 MaxHeapSize 2147483648 (2048.0MB) //最大堆大小为2048M NewSize 805306368 (768.0MB) MaxNewSize 805306368 (768.0MB) //年轻代大小为768M OldSize 1342177280 (1280.0MB) //老年代代大小为1280M NewRatio 8 //这个有点自相矛盾1:8 SurvivorRatio 3 //救助区大小占整个年轻代的五分之一 PermSize 268435456 (256.0MB) //持久代大小为256M MaxPermSize 268435456 (256.0MB) //持久代大小为256M Heap Usage:
//年轻代大小这里只计算了一个救助区所以少了153M
New Generation (Eden 1 Survivor Space): capacity 644284416 (614.4375MB) used 362446760 (345.65616607666016MB) free 281837656 (268.78133392333984MB) 56.25570803810968% used
//Eden Space大小为614.43-153460.8M
Eden Space: capacity 483262464 (460.875MB) used 342975440 (327.0868682861328MB) free 140287024 (133.7881317138672MB) 70.97084204743864% used
//两个救助区的大小均为153MB 与前面的SurvivorRatio参数设置值计算结果一致。
From Space: capacity 161021952 (153.5625MB) used 19471320 (18.569297790527344MB) free 141550632 (134.99320220947266MB) 12.092338813530219% used
To Space: capacity 161021952 (153.5625MB) used 0 (0.0MB) free 161021952 (153.5625MB) 0.0% used
//老年代大小为1280M和根据参数配置计算的结果一致。
concurrent mark-sweep generation: capacity 1342177280 (1280.0MB) used 763110504 (727.7588882446289MB) free 579066776 (552.2411117553711MB) 56.85616314411163% used
//永久代大小为256M实际使用不到50%。可在系统运行一段时间后稳定该值。
Perm Generation: capacity 268435456 (256.0MB) used 118994736 (113.48222351074219MB) free 149440720 (142.5177764892578MB) 44.32899355888367% used
8.2Top命令监控结果 通过使用top命令进行持续监控发现此时CPU空闲比例为85.7%剩余物理内存为3619M虚拟内存8G未使用。持续的监控结果显示进程29003占用系统内存不断在增加已经快得到最大值。
8.3Jstat命令监控结果 使用jstat命令对PID为29003的进程进行gc回收情况检查发现由于Old段的内存使用量已经超过了设定的80的警戒线导致系统每隔一两秒就进行一次FGCFGC的次数明显多余YGC的次数但是每次FGC后old的内存占用比例却没有明显变化—系统尝试进行FGC也不能有效地回收这部分对象所占内存。同时也说明年轻代的参数配置可能有问题导致大部分对象都不得不放到老年代来进行FGC操作这个或许跟系统配置的会话失效时间过长有关。
8.4Jstack打印出的堆栈内容 在上图中发现大量的的工作流线程锁定。 在上图中发现大量的的cms线程池管理线程锁定。
9.原因分析
通过对jvm内存进行实时监控后发现导致老年代内存不能有效回收的原因就在于堆栈中存在大量的线程死锁问题。建议开发组认真审查com.zzxy.workflow包的源代码以及com.web.csm包中的源代码看看是否存在线程死锁的缺陷。
9.1该系统的JVM设置
jvm-options-XX:PrintGCApplicationConcurrentTime/jvm-options jvm-options-XX:PrintGCApplicationStoppedTime/jvm-options
jvm-options-XX:PrintGCTimeStamps/jvm-options
jvm-options-XX:PrintGCDetails/jvm-options
jvm-options-Xms2048m/jvm-options
jvm-options-Xmx2048m/jvm-options
jvm-options-server/jvm-options
jvm-options-Djava.awt.headlesstrue/jvm-options
jvm-options-XX:PermSize256m/jvm-options
jvm-options-XX:MaxPermSize256m/jvm-options
jvm-options-XX:DisableExplicitGC/jvm-options
jvm-options-Xmn768M/jvm-options
jvm-options-XX:SurvivorRatio3/jvm-options
jvm-options-Xss128K/jvm-options
jvm-options-XX:TargetSurvivorRatio80/jvm-options
jvm-options-XX:MaxTenuringThreshold5/jvm-options
jvm-options-XX:UseConcMarkSweepGC/jvm-options
jvm-options-XX:CMSClassUnloadingEnabled/jvm-options
jvm-options-XX:UseCMSCompactAtFullCollection/jvm-options
jvm-options-XX:-CMSParallelRemarkEnabled/jvm-options
9.2后记
1、性能调优要做到有的放矢根据实际业务系统的特点以一定时间的JVM日志记录为依据进行有针对性的调整、比较和观察。
2、性能调优是个无止境的过程要综合权衡调优成本和更换硬件成本的大小使用最经济的手段达到最好的效果。
3、性能调优不仅仅包括JVM的调优还有服务器硬件配置、操作系统参数、中间件线程池、数据库连接池、数据库本身参数以及具体的数据库表、索引、分区等的调整和优化。
4、通过特定工具检查代码中存在的性能问题并加以修正是一种比较经济快捷的调优方法。
附舍得网的典型配置
$JAVA_ARGS . -Dresin.home$SERVER_ROOT
-server
-Xms6000M
-Xmx6000M
-Xmn500M
-XX:PermSize500M
-XX:MaxPermSize500M
-XX:SurvivorRatio65536
-XX:MaxTenuringThreshold0
-Xnoclassgc
-XX:DisableExplicitGC
-XX:UseParNewGC
-XX:UseConcMarkSweepGC
-XX:UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction0
-XX:CMSClassUnloadingEnabled
-XX:-CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction90
-XX:SoftRefLRUPolicyMSPerMB0
-XX:PrintClassHistogram
-XX:PrintGCDetails
-XX:PrintGCTimeStamps
-XX:PrintHeapAtGC
-Xloggc:log/gc.log ; 说明
1、 -XX:SurvivorRatio65536 -XX:MaxTenuringThreshold0就是去掉了救助空间
2、-Xnoclassgc禁用类垃圾回收性能会高一点
3、-XX:DisableExplicitGC禁止System.gc()免得程序员误调用gc方法影响性能
4、-XX:UseParNewGC对年轻代采用多线程并行回收这样收得快