新上线一个java服务,或者是RPC或者是WEB站点, 内存的设置该怎么设置呢?设置成多大比较合适,既不浪费内存,又不影响性能呢?
分析:
依据的原则是根据Java Performance里面的推荐公式来进行设置。
具体来讲:
Java整个堆大小设置,Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍
永久代 PermSize和MaxPermSize设置为老年代存活对象的1.2-1.5倍。
年轻代Xmn的设置为老年代存活对象的1-1.5倍。
老年代的内存大小设置为老年代存活对象的2-3倍。
BTW:
1、Sun官方建议年轻代的大小为整个堆的3/8左右, 所以按照上述设置的方式,基本符合Sun的建议。
2、堆大小=年轻代大小+年老代大小, 即xmx=xmn+老年代大小 。 Permsize不影响堆大小。
3、为什么要按照上面的来进行设置呢? 没有具体的说明,但应该是根据多种调优之后得出的一个结论。
如何确认老年代存活对象大小?
JVM参数中添加GC日志,GC日志中会记录每次FullGC之后各代的内存大小,观察老年代GC之后的空间大小。可观察一段时间内(比如2天)的FullGC之后的内存情况,根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小(可根据多次FullGC之后的内存大小取平均值)
对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数
1.监控GC的状态
使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化。
举一个例子: 系统崩溃前的一些现象:
之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值,这个时候就需要分析JVM内存快照dump。
2.生成堆的dump文件
通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。
3.分析dump文件
打开这个3G的堆信息文件,显然一般的Window系统没有这么大的内存,必须借助高配置的Linux,几种工具打开该文件:
备注:文件太大,建议使用Eclipse专门的静态内存分析工具Mat打开分析。
4.分析结果,判断是否需要优化
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化,如果GC时间超过1-3秒,或者频繁GC,则必须优化。
注:如果满足下面的指标,则一般不需要进行GC:
5.调整GC类型和内存分配
如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择。
6.不断的分析和调整
通过不断的试验和试错,分析并找到最合适的参数,如果找到了最合适的参数,则将这些参数应用到所有服务器。
1.针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值;
2.年轻代和年老代将根据默认的比例(1:2)分配堆内存, 可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代。
比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。
3.年轻代和年老代设置多大才算合理
1)更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
2)更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率
如何选择应该依赖应用程序对象生命周期的分布情况: 如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性。
在抉择时应该根 据以下两点:
(1)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 。
(2)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间。
4.在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC 。
5.线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。
理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器,更加关注吞吐量
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:+UseG1GC:启用G1垃圾回收器
-Xms2560m |
堆初始大小为2560M |
-Xmx2560m |
堆最大为2560M |
-Xss512k |
每个线程堆栈大小为512K |
-XX:PermSize=128m |
持久代(perm gen)初始大小为128M |
-XX:MaxPermSize=384m |
持久代最大为384M |
-XX:NewSize=1536m |
新生代初始大小为1536M |
-XX:MaxNewSize=1536m |
新生代最大为1536M |
-XX:SurvivorRatio=22 |
新生代Eden区与Survivor区的比值,因为新生代大小为1536,Eden区大小为1536*22/(22+2)=1408,每个Survivor区为64M |
-XX:+UseParNewGC |
新生代垃圾收集器为ParNew,可与CMS同时使用 |
-XX:ParallelGCThreads=4 |
并行收集的线程数为4 |
-XX:MaxTenuringThreshold=9 |
对象晋升老年代的年龄阈值为9 |
-XX:+UseConcMarkSweepGC |
老年代使用CMS垃圾收集器 |
-XX:+DisableExplicitGC |
禁用System.gc() |
-XX:+UseCMSInitiatingOccupancyOnly |
指定HotSpot VM总是使用-XX:CMSInitiatingOccupancyFraction的值作为老年代使用率限制来启动CMS垃圾回收。如果没有使用-XX:+UseCMSInitiatingOccupancyOnly,那么HotSpot VM只是利用CMSInitiatingOccupancyFraction启动第一次CMS垃圾回收,后面都是使用HotSpot VM自动计算出来的值 |
-XX:+ScavengeBeforeFullGC |
在Full GC前触发一次Minor GC |
-XX:+UseCMSCompactAtFullCollection |
FULL GC时对老年代进行压缩。CMS默认不会移动内存,因此容易产生碎片。增加该参数虽然会影响性能,但可以消除碎片 |
-XX:+CMSParallelRemarkEnabled |
开启并行标记,减少停顿时间 |
-XX:CMSFullGCsBeforeCompaction=9 |
9次以后进行内存压缩。由于并发收集器不对内存空间进行压缩整理,所以运行一段时间以后会产生"碎片",使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩整理 |
-XX:CMSInitiatingOccupancyFraction=60 |
老年代使用率达到60%时,触发GC回收 |
-XX:+CMSClassUnloadingEnabled |
垃圾回收会清理持久代,移除不再使用的classes。这个参数只有在UseConcMarkSweepGC也启用的情况下才有用 |
-XX:SoftRefLRUPolicyMSPerMB=0 |
每MB堆中软引用(soft reference)对象的存活时间。软引用用于描述一些有用但并非必须的对象。对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围内进行二次回收。如果这次回收还没有足够内存,才会抛出内存溢出异常 |
-XX:-ReduceInitialCardMarks |
为解决jdk 6u18 放入大对象导致jvm crash的一个配置参数 |
-XX:+CMSPermGenSweepingEnabled |
允许对持久代进行清理 |
-XX:CMSInitiatingPermOccupancyFraction=70 |
持久代使用率超过70%触发GC |
-XX:+ExplicitGCInvokesConcurrent |
命令JVM无论什么时候调用系统GC,都执行CMS GC,而不是Full GC (一个case) |
-XX:+PrintGCDetails |
打印GC详情 |
-XX:+PrintGCTimeStamps |
打印以JVM启动时间为基准的相对时间 |
-XX:+PrintGCApplicationConcurrentTime |
打印每次垃圾回收前,程序未中断的执行时间 |
-XX:+PrintHeapAtGC |
打印GC前后的详细堆栈信息 |
-Xloggc:/data/applogs/heap_trace.txt |
日志信息地址 |
-XX:-HeapDumpOnOutOfMemoryError |
在java.lang.OutOfMemoryError异常出现时,输出一个dump.core文件,记录当时的堆内存快照 |
-XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError |
堆内存快照地址 |