一次JVM调优引发的连锁反应

2016年10月份遇到过一次oom,那时时是内存溢出问题,已解决。2017年3月份,4月份有各遇到一次oom故障,需要汇总下这方面知识了。 这里可能有很多都是copy别人的,总结自己和汇总别人的方方面面,以备查用。

涉及知识点:


jps,jstack以及线程各个状态介绍 ,top,jstat,jmap,mat以及插件,内存泄漏,内存溢出,gc,heap,young,
old区,jvm参数
jps -v
top -h -pPid
jstack -l pid >file
jstat -gcutil pid
jmap -histo[:live] pid
jmap -heap pid
jmap -dump:format=b,file=fileName pid

一:jps

jps是jdk提供的一个查看当前Java进程的小工具 jps -v
-v:输出jvm参数 jps -v | grep invite : 输出启动的invite关键词的服务的进程号和相关参数。

二:jstack

查看占用cpu过高的有问题代码==》定位出程序中的性能问题 jstack 查看线程 得知当前线程的运行

1.通过top命令查看当前CPU情况—- 》占用CPU过高,下面来排查是什么线程的什么代码导致CPU过高

  1. top -H -p155121 -H 指显示线程,-p 是指定进程 —》 得到CPU占用较高的线程,记下pid

1

从十进制转成十六进制表示 155166–>25e1e

通过jstack命令获取当前线程栈 jstack -l pid(进程pid) jstack -l 155121 > jstack.txt 查找nid=0x25e1e的线程栈

2

这里会显示对应的代码不合理之处。 然后到对应的代码中可查看不合理的逻辑。

该方法定位出程序中的性能问题 jstack Dump 日志文件中的线程状态 dump 文件里,值得关注的线程状态有:

1. 死锁,Deadlock(重点关注) 
2. 执行中,Runnable   
3. 等待资源,Waiting on condition(重点关注) 
4. 等待获取监视器,Waiting on monitor entry(重点关注)
5. 暂停,Suspended
6. 对象等待中,Object.wait() 或 TIMED_WAITING
7. 阻塞,Blocked(重点关注)  
8. 停止,Parked
9. 具体线程状态分析:http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html

补充:vim打开后 用 / 找到了一个词,那么 n N 就分别是向后,向前再找这个词。

三:jstat

jstat命令查看jvm的GC情况 性能分析

jstat -gcutil 167367 2000

会每2000秒一次显示进程号为167367 的java进成的GC情况 3

S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比 
O:old代已使用的占当前容量百分比 
P:perm代已使用的占当前容量百分比         
YGC:从应用程序启动到采样时年轻代中gc次数 
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数 
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s) 
GCT:从应用程序启动到采样时gc用的总时间(s) 

在线上的应用中通过会指定CMSInitiatingOccupancyFraction这个参数来指定当老年代使用了百分之多少的时候,通过CMS进行FGC,当然这个参数需要和这些参数一起使用“-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly”,CMSInitiatingOccupancyFraction的默认值是68,现在中文站线上的应用都是70,也就是说当老年代使用率真达到或者超过70%时,就会进行FGC。

四:jmap

jmap:得到运行java程序的内存分配的详细情况

jmap -heap 16737 : 167367进程id

4

  1. jmap -heap pid
  2. 查看java 堆(heap)使用情况

using thread-local object allocation. 
Parallel GC with 4 thread(s) //GC 方式 Heap Configuration: //堆内存初始化配置 
MinHeapFreeRatio=40 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)

MaxHeapFreeRatio=70 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
  MaxHeapSize=512.0MB //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 1.0MB //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小

MaxNewSize =4095MB //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
  OldSize = 4.0MB //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
  NewRatio = 8 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
8=2survivor/Eden  ==> surviver/Eden = 4

PermSize= 16.0MB //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
MaxPermSize=64.0MB //对应jvm启动参数-XX:MaxPermSize= :设置JVM堆的‘永生代’的最大大小
Heap Usage: //堆内存分步 
PS Young Generation 
Eden Space: //Eden区内存分布

capacity = 20381696 (19.4375MB) //Eden区总容量
used = 20370032 (19.426376342773438MB) //Eden区已使用

free = 11664 (0.0111236572265625MB) //Eden区剩余容量
99.94277218147106% used //Eden区使用比率 
From Space: //其中一个Survivor区的内存分布

capacity = 8519680 (8.125MB)
      used = 32768 (0.03125MB)
      free = 8486912 (8.09375MB)
0.38461538461538464% used 
To Space: //另一个Survivor区的内存分布 
capacity = 9306112 (8.875MB) 
used = 0 (0.0MB)

free = 9306112 (8.875MB)
      0.0% used
PS Old Generation //当前的Old区内存分布

capacity = 366280704 (349.3125MB)
      used = 322179848 (307.25464630126953MB)
      free = 44100856 (42.05785369873047MB)
      87.95982001825573% used
PS Perm Generation //当前的 “永生代” 内存分布 
capacity = 32243712 (30.75MB) 
used = 28918584 (27.57891082763672MB)

free = 3325128 (3.1710891723632812MB)
89.68751488662348% used

查看内存泄漏方式1:

-histo:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。num表示排列的序号。

-histo:live 167367 :同上,但是只打印存活对象的情况。

根据上述命令可分析出是否存在内存泄漏情况。

jmap -histo 167367 head -n 10

5

6

#instance 是对象的实例个数 
#bytes 是总占用的字节数 
class name 对应的就是 Class 文件里的 class 的标识 
B 代表 byte
C 代表 char
D 代表 double
F 代表 float
I 代表 int
J 代表 long
Z 代表 boolean
前边有 [ 代表数组, [I 就相当于 int[]
对象用 [L+ 类名表示
输出中[C对象占用Heap这么多,往往跟String有关,String其内部使用final char[]数组来保存数据的。

constMethodKlass/ methodKlass/ constantPoolKlass/ 与Classloader相关,常驻与Perm区。 看出目前堆内存中自己程序中的对象实例以及占用bytes,从而确定程序中哪些位置对这些对象进行创建使用且未被释放(典型的情况是将对象存入List等集合类而未被remove)。 也通过多次执行jmap -histo:live pid >mem.txt将每次结果定向到不同的txt中,然后对象对比。发现对象实例的增长与占用字节的变化。

查看内存泄漏方式2:

jmap -dump:format=b,file=mem.dat pid #将内存使用的详细情况输出到mem.dat 文件 7

通过jhat -port 7000 mem.dat可以将mem.dat的内容以web的方式暴露到网络,访问http://ip-server:7000查看。

把内存结构全部dump到二进制文件中,用eclipse的MemoryAnalyzer都可以分析内存结构。

可用此eclipse分析内存泄漏.

优化1: 1)首先配置JVM启动参数,让JVM在遇到OutOfMemoryError时自动生成Dump文件 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path


然后使用MAT分析工具,如jhat命令,eclipse的mat插件。
最后在eclipse中安装MAT插件(http://www.eclipse.org/mat/),然后在eclipse中,file---->open,
打开这个文件heap.bin,利用现成的OOM工具进行分析
 用jhat命令可以参看 jhat -port 5000 heapDump 在浏览器中访问:http://localhost:5000/ 查看详细信息

这个时候将dump出的文件在ECLIPSE中打开,使用MAT进行分析(ECLIPSE需要先安装MAT插件)

总结: 总结 1.如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。 2.要制作堆Dump可以直接使用jvm自带的jmap命令 3.可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。 4.使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。 5.也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容 6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。

五:mat以及插件


.MAT插件安装:  eclipse插件安装很简单,点击help ---install new software------add
然后添加新地址,路径为:http://download.eclipse.org/mat/1.6/update-site/   
装完成之后,为了更有效率的使用
MAT,我们还需要做一些配置工作。因为通常而言,分析一个堆转储文件需要消耗很多的堆空间,
为了保证分析的效率和性能,在有条件的情况下,我们会建议分配给 MAT 尽可能多的内存资源:
编辑文件 MemoryAnalyzer.ini,在里面添加类似信息 -vmargs – Xmx4g。
open eclipse-> open-> file->

具体分析见: http://tivan.iteye.com/blog/1487855

六: top

top看到us,sy占比:
cpu的占有线程类型总的来说分为两种:
us :用户空间占用CPU百分比
sy :内核空间占用CPU百分比
在linux下可以通过top命令查看详细,示例如下:

一般来讲CPU us高的解决方法:


CPU us 高的原因主要是执行线程不需要任何挂起动作,且一直执行,
导致CPU 没有机会去调度执行其他的线程。
CPU sy高的解决方法:
CPU sy 高的原因主要是线程的运行状态要经常切换,对于这种情况,
常见的一种优化方法是减少线程数。

系统优化

如果找出了哪些功能点占用cpu高,接下来就需要优化了,可以从业务和技术手段两方面来进行,
平时工作中比较常用的技术手段:
弹性时间:对高使用率的请求,分散到不同的时间,比如采用队列或异步,减少同一时间处理的请求。
批处理或定时任务:把请求组合成批,这样可以使得时间真真的都有效的用在了处理,
而不是网络传输等准备工作上。(减少网络传输、数据库连接、socket连接)
缓存:将结果缓存起来,空间换时间。

如果是gc线程比较费时,则需要进一步的定位:

  首先查看一下gc策略是否合理,然后用命令jmap -F -dump:live,file=jmap.hprof [PID]
  导出内存dump文件。用Eclipse Memory Analyzer(
  MAT)分析导出来的文件,分析是哪个类占用内存比较多,分析出可能存在内存泄露的地方。
  
  注意jvm分配内存时一个大对象的分配比多个小对象的分配效率要低,如果对象比较大
  ,进行拆分能提高效率,具体原因如下:
  Java对象所占用的内存主要从堆上进行分配,堆是所有线程共享的,因此在堆上分配内存
  时需要进行加锁,这导致了创建对象开销比较大。当堆上空间不足时,会触发GC,如果
  GC后空间仍然不足,则抛出OutOfMemory错误信息。Sun JDK为了提升内存分配的效率,会为每个新创建的线程在新生代的Eden
  Space上分配一块独立的空间,这块空间称为TLAB(Thread Local Allocation
  Buffer),其大小由JVM根据运行情况计算而得,可通过-XX:TLABWasteTargetPercent来
  设置TLAB可占用的Eden Space的百分比,默认值为1%。JVM将根据这个比率、线程数量及
  线程是否频繁分配对象来给每个线程分配合适大小的TLAB空间
  。在TLAB上分配内存时不需要加锁,因此JVM在给线程中的对象分配内存时会尽量在TLAB上
  分配,如果对象过大或TLAB空间已用完,则仍然在堆上进行分配,因此在编写Java程序时,
  通常多个小的对象比大的对象分配起来更加高效。

七:jvm参数

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

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

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

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

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

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

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

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

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

-XX:+UseParallelGC -XX:ParallelGCThreads=20 
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,
年轻代使用并发收集,而年老代仍旧使用串行收集。
XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收
。此值最好配置与处理器数目相等。 

-XX:+UseParallelOldGC
-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集
-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的
配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系
统配置自行设置,所以无需再设置此值。 
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片 

常见JVM参数配置汇总

堆设置


-Xms:初始堆大小 

-Xmx:最大堆大小 

-XX:NewSize=n:设置年轻代大小 

-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代
占整个年轻代年老代和的1/4 

-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。
如:3,表示 Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5 

-XX:MaxPermSize=n:设置持久代大小 

收集器设置 

-XX:+UseSerialGC:设置串行收集器 

-XX:+UseParallelGC:设置并行收集器 

-XX:+UseParalledlOldGC:设置并行年老代收集器 

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

-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行
收集线程数


较小堆引起的碎片问题 

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

-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。 

-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对
年老代进行压缩

1: heap size 

a: -Xmx<n>                      

指定 jvm 的最大 heap 大小 , 如 :-Xmx=2g 

b: -Xms<n>                      

指定 jvm 的最小 heap 大小 , 如 :-Xms=2g , 高并发应用, 建议和-Xmx一样, 防止因为内存收缩/突然增大带来的性能影响。 

c: -Xmn<n>                      

指定 jvm 中 New Generation 的大小 , 如 :-Xmn256m。 这个参数很影响性能, 如果你的
程序需要比较多的临时内存, 建议设置到512M, 如果用的少, 尽量降低这个数值, 一般
来说128/256足以使用了。 

d: -XX:PermSize=<n> 

指定 jvm 中 Perm Generation 的最小值 , 如 :-XX:PermSize=32m。 这个参数需要看你
的实际情况,。 可以通过jmap 命令看看到底需要多少。 

e: -XX:MaxPermSize=<n>          

指定 Perm Generation 的最大值 , 如 :-XX:MaxPermSize=64m 

f: -Xss<n>                      

指定线程桟大小 , 如 :-Xss128k, 一般来说,webx框架下的应用需要256K。 如果你
的程序有大规模的递归行为, 请考虑设置到512K/1M。 这个需要全面的测试才能知道。
不过, 256K已经很大了。 这个参数对性能的影响比较大的。 

g: -XX:NewRatio=<n> 

指定 jvm 中 Old Generation heap size 与 New Generation 的比例 , 在使用 CMS GC 
的情况下此参数失效 , 如 :-XX:NewRatio=2 

h: -XX:SurvivorRatio=<n> 

指定 New Generation 中 Eden Space 与一个 Survivor Space 的 heap size 比例 ,
-XX:SurvivorRatio=8, 那么在总共 New Generation 为 10m 的情况下 ,Eden Space 为 8m 

i: -XX:MinHeapFreeRatio=<n> 

指定 jvm heap 在使用率小于 n 的情况下 ,heap 进行收缩 ,Xmx==Xms 的情况下无效 , 
如 :-XX:MinHeapFreeRatio=30 

j: -XX:MaxHeapFreeRatio=<n> 

指定 jvm heap 在使用率大于 n 的情况下 ,heap 进行扩张 ,Xmx==Xms 的情况下无效 , 
如 :-XX:MaxHeapFreeRatio=70 

k: -XX:LargePageSizeInBytes=<n> 

指定 Java heap 的分页页面大小 , 如 :-XX:LargePageSizeInBytes=128m 

2: garbage collector 

a: -XX:+UseParallelGC 

指定在 New Generation 使用 parallel collector, 并行收集 , 暂停 app threads, 
同时启动多个垃圾回收 thread, 不能和 CMS gc 一起使用 . 系统吨吐量优先 , 但是会
有较长长时间的 app pause, 后台系统任务可以使用此 gc 

b: -XX:ParallelGCThreads=<n> 

指定 parallel collection 时启动的 thread 个数 , 默认是物理 processor 的个数 , 

c: -XX:+UseParallelOldGC 

指定在 Old Generation 使用 parallel collector 

d: -XX:+UseParNewGC 

指定在 New Generation 使用 parallel collector, 是 UseParallelGC 的 gc 的升级版本 ,
有更好的性能或者优点 , 可以和 CMS gc 一起使用 

e: -XX:+CMSParallelRemarkEnabled 

在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间 

f: -XX:+UseConcMarkSweepGC 

指定在 Old Generation 使用 concurrent cmark sweep gc,gc thread 和 app thread 并行
( 在 init-mark 和 remark 时 pause app thread). app pause 时间较短 , 适合交互性强的系统 , 如 web server 

g: -XX:+UseCMSCompactAtFullCollection 

在使用 concurrent gc 的情况下 , 防止 memory fragmention, 对 live object 进行整理 , 
使 memory 碎片减少 

h: -XX:CMSInitiatingOccupancyFraction=<n> 

指示在 old generation 在使用了 n% 的比例后 , 启动 concurrent collector, 
默认值是 68, 如 :-XX:CMSInitiatingOccupancyFraction=70 

有个 bug, 在低版本(1.5.09 and early)的 jvm 上出现 , http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486089 

i: -XX:+UseCMSInitiatingOccupancyOnly 

指示只有在 old generation 在使用了初始化的比例后 concurrent collector 启动收集 
3:others 

a: -XX:MaxTenuringThreshold=<n> 

指定一个 object 在经历了 n 次 young gc 后转移到 old generation 区 , 在 linux64 的
java6 下默认值是 15, 此参数对于 throughput collector 无效 , 如 :-XX:MaxTenuringThreshold=31 

b: -XX:+DisableExplicitGC 

禁止 java 程序中的 full gc, 如 System.gc() 的调用. 最好加上么, 防止程序在代码里误用了
。对性能造成冲击。 

c: -XX:+UseFastAccessorMethods 

get,set 方法转成本地代码 

d: -XX:+PrintGCDetails 

打应垃圾收集的情况如 : 

[GC 15610.466: [ParNew: 229689K->20221K(235968K), 0.0194460 secs] 
1159829K->953935K(2070976K), 0.0196420 secs] 

e: -XX:+PrintGCTimeStamps 

打应垃圾收集的时间情况 , 如 : 

[Times: user=0.09 sys=0.00, real=0.02 secs] 

f: -XX:+PrintGCApplicationStoppedTime 

打应垃圾收集时 , 系统的停顿时间 , 如 : 

Total time for which application threads were stopped: 0.0225920 seconds 

4: a web server product sample and process 

JAVA_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled 
-XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " 

最初的时候我们用 UseParallelGC 和 UseParallelOldGC,heap 开了 3G,NewRatio 设成 1. 这样的
配置下 young gc 发生频率约 12,3 妙一次 , 平均每次花费 80ms 左右 ,full gc 发生的频率极低 ,
每次消耗 1s 左右 . 从所有 gc 消耗系统时间看 , 系统使用率还是满高的 , 但是不论是 young gc 
还是 old gc,applicaton thread pause 的时间比较长 , 不合适 web 应用 . 我们也调小 
New Generation 的 , 但是这样会使 full gc 时间加长 . 

后来我们就用 CMS gc(-XX:+UseConcMarkSweepGC), 当时的总 heap 还是 3g, 新生代 1.5g 后 , 
观察不是很理想 , 改为 jvm heap 为 2g 新生代设置 -Xmn1g, 在这样的情况下 young gc 发生的
频率变成 ,7,8 妙一次 , 平均每次时间 40~50 毫秒左右 ,CMS gc 很少发生 , 每次时间在 init-mark
和 remark(two steps stop all app thread) 总共平均花费 80~90ms 左右 . 

在这里我们曾经 New Generation 调大到 1400m, 总共 2g 的 jvm heap, 平均每次 ygc 花费
时间 60~70ms 左右 ,CMS gc 的 init-mark 和 remark 之和平均在 50ms 左右 , 这里我们意识
到错误的方向 , 或者说 CMS 的作用 , 所以进行了修改 

最后我们调小 New Generation 为 256m,young gc 2,3 秒发生一次 , 平均停顿时间在 25 毫秒左右 ,
CMS gc 的 init-mark 和 remark 之和平均在 50ms 左右 , 这样使系统比较平滑 , 经压力测试 ,
这个配置下系统性能是比较高的 

在使用 CMS gc 的时候他有两种触发 gc 的方式 :gc 估算触发和 heap 占用触发 . 
我们的 1.5.0.09 环境下有次 old 区 heap 占用再 30% 左右 , 她就频繁 gc, 
个人感觉系统估算触发这种方式不靠谱 , 还是用 heap 使用比率触发比较稳妥 . 

这些数据都来自 64 位测试机 , 过程中的数据都是我在 jboss log 找的 , 
当时没有记下来 , 可能存在一点点偏差 , 但不会很大 , 基本过程就是这样 . 

5: 总结

web server 作为交互性要求较高的应用 , 我们应该使用 Parallel+CMS,UseParNewGC 这个在 jdk6 -server 上是默认的 ,new generation gc, 新生代不能太大 , 这样每次 pause 会短一些 .CMS mark-sweep generation 可以大一些 , 可以根据 pause time 实际情况控制。

七:补充

1、内存泄露,对象已经死了,无法通过垃圾收集器进行自动回收,通过找出泄露的代码位置和原因,才好确定解决方案; 内存泄漏的原因分析

总结出来只有一条: 存在无效的引用! 良好的模块设计以及合理使用设计模式有助于解决此问题。 2、内存溢出,内存中的对象都还必须存活着,这说明Java堆分配空间不足,检查堆设置大小(-Xmx与-Xms),检查代码是否存在对象生命周期太长、持有状态时间过长的情况。



版权申明

知识共享许可协议
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。 转载文章请注明原文出处。

天道酬勤
评分4.8/5 based on 20