高并发系统的问题

1.数据一致性

2.低延时

3.可扩展

4.高性能、稳定顺滑

要求服务RT没有抖动,cpu不会负载突然升高,一般问题是GC,Jit编译,代码性能,日志打印block,磁盘打满。

导致cpu飙高到原因可能是jvm JIT编译导致

线上发布(特别是流量较大时)时候,我们都会碰到一个程序"预热"的问题,原因是:JVM在应用刚启动时,先开始的是字节码的解释执行,直到执行频率到了一定阶段才会开始编译执行。这其中由于刚开始的解释执行较慢,流量刚进来的几秒钟CPU飙高,此时RT也瞬间飙高,而且如果这时候解释执行将CPU占满,将导致没有机会去编译执行,长时间在解释执行,系统会瘫痪

å¾ 1. JIT å·¥ä½åçå¾

https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/index.html)也可能存在编译执行的过程中,cpu飙高。

上面两种情况都可以通过jwarmup解决,JWarmUp的基本原理是根据上次程序运行的情况,记录下热点方法、类编译顺序等信息,在应用下次启动的时候积极加载相关的类,并积极编译相关的方法,进而达到应用启动后直接执行编译好的native code的目的。

注:JitWarmUp的原理是记录编译的方法到日志文件中,所以beta发布的那台机器必须是有流量的,Recording时间不要太短,尽量多编译一些方法。

GC问题需要JVM参数调优了,

比如

  1. 增加日志开关,减少线上不必要的日志打印。(done)
  2. 采用异步日志打印的方式,设置合理的队列长度。(done)
  3. 进行JVM参数调优,降低Minor GC的频率和Full GC STW的时间: 老应用原有的参数如下:(各个参数含义http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
-server -Xmx4g -Xms4g -Xmn1536m 
-XX:PermSize=128m -XX:MaxPermSize=256m 
-Xss256k 
-XX:+DisableExplicitGC 
-XX:+UseConcMarkSweepGC 
-XX:+CMSParallelRemarkEnabled 
-XX:+UseCMSCompactAtFullCollection 
-XX:+UseFastAccessorMethods 
-XX:+UseCMSInitiatingOccupancyOnly 
-XX:+UseCompressedOops

修改为:

-server -Xms4g -Xmx4g -Xmn2g -Xss256k 
-XX:MaxNewSize=256m -XX:MaxPermSize=512m 
-XX:MaxDirectMemorySize=1g 
-XX:SurvivorRatio=10 
-XX:+UseConcMarkSweepGC 
-XX:+UseCMSCompactAtFullCollection 
-XX:CMSMaxAbortablePrecleanTime=5000 
-XX:+CMSClassUnloadingEnabled 
-XX:CMSInitiatingOccupancyFraction=80 
-XX:+UseCMSInitiatingOccupancyOnly 
-XX:+ExplicitGCInvokesConcurrent 
-Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000 
-XX:ParallelGCThreads=4 -XX:ConcGCThreads=4 
-Xloggc:/home/admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs/java.hprof

核心变化在于增加了Eden区的大小,提高了CMS老年代垃圾回收的阈值,设置了垃圾回收的并发数量。目的就在于减少Minor GC的频率,缩短STW的时间,尽量降低GC对系统的影响。

JVM经验&&规则

  1. 年轻代大小选择
    • 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,年轻代收集发生的频率也是最小的.同时,减少到达年老代的对象.
    • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度.因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用.避免设置过小.当新生代设置过小时会导致:1.YGC次数更加频繁 2.可能导致YGC对象直接进入旧生代,如果此时旧生代满了,会触发FGC.
  2. 年老代大小选择
    1. 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数.如果堆设置小了,可以会造成内存碎 片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间.最优化的方案,一般需要参考以下数据获得:
      并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收上的时间比例。
    2. 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象.
  3. 较小堆引起的碎片问题
    因为年老代的并发收集器使用标记,清除算法,所以不会对堆进行压缩.当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象.但是,当堆空间较小时,运行一段时间以后,就会出现"碎片",如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记,清除方式进行回收.如果出现"碎片",可能需要进行如下配置:
    -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩.
    -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
  4. 用64位操作系统,Linux下64位的jdk比32位jdk要慢一些,但是吃得内存更多,吞吐量更大
  5. XMX和XMS设置一样大,MaxPermSize和MinPermSize设置一样大,这样可以减轻伸缩堆大小带来的压力
  6. 使用CMS的好处是用尽量少的新生代,经验值是128M-256M, 然后老生代利用CMS并行收集, 这样能保证系统低延迟的吞吐效率。 实际上cms的收集停顿时间非常的短,2G的内存, 大约20-80ms的应用程序停顿时间
  7. 系统停顿的时候可能是GC的问题也可能是程序的问题,多用jmap和jstack查看,或者killall -3 java,然后查看java控制台日志,能看出很多问题。(相关工具的使用方法将在后面的blog中介绍)
  8. 仔细了解自己的应用,如果用了缓存,那么年老代应该大一些,缓存的HashMap不应该无限制长,建议采用LRU算法的Map做缓存,LRUMap的最大长度也要根据实际情况设定。
  9. 采用并发回收时,年轻代小一点,年老代要大,因为年老大用的是并发回收,即使时间长点也不会影响其他程序继续运行,网站不会停顿
  10. JVM参数的设置(特别是 –Xmx –Xms –Xmn -XX:SurvivorRatio  -XX:MaxTenuringThreshold等参数的设置没有一个固定的公式,需要根据PV old区实际数据 YGC次数等多方面来衡量。为了避免promotion faild可能会导致xmn设置偏小,也意味着YGC的次数会增多,处理并发访问的能力下降等问题。每个参数的调整都需要经过详细的性能测试,才能找到特定应用的最佳配置。

猜你喜欢

转载自blog.csdn.net/maso88/article/details/88292296
今日推荐