记一次JVM老生代增长过快问题排查

2023-10-08 06:20

本文主要是介绍记一次JVM老生代增长过快问题排查,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

版本上线后,观察了几天,程序猿发现一个异常现象,之前一直非常平稳的JVM老生代突然在上线后以有了明显的增长,而且是持续的增长。于是开始了这次老生代过快增长的问题排查......

揪出导致老生代快速增长的对象分析内存对象

先得找个好用的工具,唯品会开源的JVM工具箱vjtools是个不错的选择,可以将JVM新老生代的各个对象实例个数和大小的Histgram打印出来。

想看出老生代里是什么对象在不断增长,使用vjtools其中的vjmap分别dump两天的老生代对象实例出来。第一天的dump:

<span style="color:#606c71"><span style="color:#567482"><code>#num	#all count/bytes    #old count/bytes   #Class description
-----------------------------------------------------------------------------------
1:	       102/    1k        102/    1k   $Proxy22
2:	      7073/  283k       6403/  258k   boolean[]
3:	    200073/  157m      85594/  135m   byte[]
4:	       493/   26k        137/    4k   byte[][]
...
60:	    365800/   22m     357186/   21m   com.mysql.jdbc.ConnectionPropertiesImpl$BooleanConnectionProperty
61:	     86800/    5m      84756/    5m   com.mysql.jdbc.ConnectionPropertiesImpl$IntegerConnectionProperty
62:	      3100/  193k       3027/  189k   com.mysql.jdbc.ConnectionPropertiesImpl$LongConnectionProperty
63:	      9300/  653k       9081/  638k   com.mysql.jdbc.ConnectionPropertiesImpl$MemorySizeConnectionProperty
64:	    127100/    7m     124107/    7m   com.mysql.jdbc.ConnectionPropertiesImpl$StringConnectionProperty
65:	      3100/    3m       3027/    3m   com.mysql.jdbc.JDBC4Connection
66:	      3126/   97k       3027/   94k   com.mysql.jdbc.JDBC4DatabaseMetaData
67:	       990/  239k        924/  223k   com.mysql.jdbc.MysqlIO
68:	      2927/   68k       2854/   66k   com.mysql.jdbc.NetworkResources
69:	      3100/   96k       3027/   94k com.mysql.jdbc.NonRegisteringDriver$ConnectionPhantomReference
</code></span></span>

从这份转储中发现JDBC4Connection这个类有些奇怪,程序里是用了连接池的,但这里的连接实例个数却有3027个,远远超过连接池配置的最大连接数,带着这个疑问,再看看第二天的转储:

<span style="color:#606c71"><span style="color:#567482"><code>#num	#all count/bytes    #old count/bytes   #Class description
-----------------------------------------------------------------------------------
1:	       102/    1k        102/    1k   $Proxy22
2:	      8407/  336k       8017/  321k   boolean[]
3:	    196962/  174m      88444/  153m   byte[]
4:	       512/   27k        190/    5k   byte[][]
...
60:	    462088/   28m     452412/   27m   com.mysql.jdbc.ConnectionPropertiesImpl$BooleanConnectionProperty
61:	    109648/    6m     107352/    6m   com.mysql.jdbc.ConnectionPropertiesImpl$IntegerConnectionProperty
62:	      3916/  244k       3834/  239k   com.mysql.jdbc.ConnectionPropertiesImpl$LongConnectionProperty
63:	     11748/  826k      11502/  808k   com.mysql.jdbc.ConnectionPropertiesImpl$MemorySizeConnectionProperty
64:	    160556/    9m     157194/    9m   com.mysql.jdbc.ConnectionPropertiesImpl$StringConnectionProperty
65:	      3916/    4m       3834/    4m   com.mysql.jdbc.JDBC4Connection
66:	      3933/  122k       3834/  119k   com.mysql.jdbc.JDBC4DatabaseMetaData
67:	      1266/  306k       1200/  290k   com.mysql.jdbc.MysqlIO
68:	      3697/   86k       3615/   84k   com.mysql.jdbc.NetworkResources
69:	      3916/  122k       3834/  119k   com.mysql.jdbc.NonRegisteringDriver$ConnectionPhantomReference
</code></span></span>

两份dump对比,不难发现老生代里明显增长的对象就是这个JDBC4Connection(其他几个都是和这个类相关引用的)。到底是哪里来的这些数据库连接呢?注:这里采用连接池是c3p0 ,版本是c3p0-0.9.5-pre8紧跟用jmap将整个堆dump下来,通过MAT进行分析,JDBC4Connection这个对象一共有3792个实例,占用了100多M内存。 图片

其中一个实例的GC Root Path:图片

可见它被两个对象引用,一个是连接池BasicResourcePool的未使用的成员变量,一个是MySQL JDBC Driver的ConnectionPhantomReference,跟踪到源码中看看:

BasicResourcePool.java

<span style="color:#606c71"><span style="color:#567482"><code><span style="color:#999988"><em>/* all valid, managed resources currently available for checkout */</em></span>
LinkedList unused <span style="color:#000000"><strong>=</strong></span> <span style="color:#000000"><strong>new</strong></span> LinkedList<span style="color:#000000"><strong>();</strong></span>
</code></span></span>

可见未使用的对象是连接池里的正常可用的连接。

NonRegisteringDriver.java

<span style="color:#606c71"><span style="color:#567482"><code><span style="color:#000000"><strong>protected</strong></span> <span style="color:#000000"><strong>static</strong></span> <span style="color:#445588"><strong>void</strong></span> <span style="color:#990000"><strong>trackConnection</strong></span><span style="color:#000000"><strong>(</strong></span>Connection newConn<span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>{</strong></span>ConnectionPhantomReference phantomRef <span style="color:#000000"><strong>=</strong></span> <span style="color:#000000"><strong>new</strong></span> ConnectionPhantomReference<span style="color:#000000"><strong>((</strong></span>ConnectionImpl<span style="color:#000000"><strong>)</strong></span> newConn<span style="color:#000000"><strong>,</strong></span> refQueue<span style="color:#000000"><strong>);</strong></span>connectionPhantomRefs<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">put</span><span style="color:#000000"><strong>(</strong></span>phantomRef<span style="color:#000000"><strong>,</strong></span> phantomRef<span style="color:#000000"><strong>);</strong></span>
<span style="color:#000000"><strong>}</strong></span>
</code></span></span>

这个虚引用是在JDBC驱动程序在构造连接时用来跟踪这个连接的,在被GC回收前做一些清理(释放资源)的事情,所以每个连接被构造出来后,都会被跟踪,这是驱动程序为了防止有资源随着连接回收而未释放的手段。

可见,这个JDBC4Connection实例是堂堂正正的连接池里的连接实例,呆在老生代是理所当然的。

再来看另一个:

图片

这个实例除了ConnectionPhantomReference引用外,还有一个也是连接池BasicResourcePool里的对象:formerResources,顾名思义:曾经在连接池里呆过的对象。

再检查其他的JDBC4Connection实例的GC根路径,大部分都是第二个这种,被以前的资源引用到的。

看到这里,基本已经清晰了,这些多出来的连接对象,应该是曾经被连接池里创建出来,用完后被抛弃掉的连接,被放到formerResources,这些对象熬过了几轮YGC,到了老生代,等着被老生代GC回收的”退休”对象。

真相渐渐浮出​​水面

继续跟进加入formerResources的相关代码:

<span style="color:#606c71"><span style="color:#567482"><code><span style="color:#000000"><strong>private</strong></span> <span style="color:#445588"><strong>void</strong></span> <span style="color:#990000"><strong>removeResource</strong></span><span style="color:#000000"><strong>(</strong></span>Object resc<span style="color:#000000"><strong>,</strong></span> <span style="color:#445588"><strong>boolean</strong></span> synchronous<span style="color:#000000"><strong>){</strong></span><span style="color:#000000"><strong>...</strong></span>unused<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">remove</span><span style="color:#000000"><strong>(</strong></span>resc<span style="color:#000000"><strong>);</strong></span>destroyResource<span style="color:#000000"><strong>(</strong></span>resc<span style="color:#000000"><strong>,</strong></span> synchronous<span style="color:#000000"><strong>,</strong></span> checked_out<span style="color:#000000"><strong>);</strong></span>addToFormerResources<span style="color:#000000"><strong>(</strong></span> resc <span style="color:#000000"><strong>);</strong></span>asyncFireResourceRemoved<span style="color:#000000"><strong>(</strong></span> resc<span style="color:#000000"><strong>,</strong></span> <span style="color:#000000"><strong>false</strong></span><span style="color:#000000"><strong>,</strong></span> managed<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">size</span><span style="color:#000000"><strong>(),</strong></span> unused<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">size</span><span style="color:#000000"><strong>(),</strong></span> excluded<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">size</span><span style="color:#000000"><strong>()</strong></span> <span style="color:#000000"><strong>);</strong></span><span style="color:#000000"><strong>...</strong></span>
<span style="color:#000000"><strong>}</strong></span><span style="color:#000000"><strong>private</strong></span> <span style="color:#445588"><strong>void</strong></span> <span style="color:#990000"><strong>cullExpired</strong></span><span style="color:#000000"><strong>(){</strong></span><span style="color:#000000"><strong>...</strong></span><span style="color:#000000"><strong>if</strong></span> <span style="color:#000000"><strong>(</strong></span> shouldExpire<span style="color:#000000"><strong>(</strong></span> resc <span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>)</strong></span><span style="color:#000000"><strong>{</strong></span><span style="color:#000000"><strong>if</strong></span> <span style="color:#000000"><strong>(</strong></span> logger<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">isLoggable</span><span style="color:#000000"><strong>(</strong></span> MLevel<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">FINER</span> <span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>)</strong></span>logger<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">log</span><span style="color:#000000"><strong>(</strong></span> MLevel<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">FINER</span><span style="color:#000000"><strong>,</strong></span> <span style="color:#dd1144">"Removing expired resource: "</span> <span style="color:#000000"><strong>+</strong></span> resc <span style="color:#000000"><strong>+</strong></span> <span style="color:#dd1144">" ["</span> <span style="color:#000000"><strong>+</strong></span> <span style="color:#000000"><strong>this</strong></span> <span style="color:#000000"><strong>+</strong></span> <span style="color:#dd1144">"]"</span><span style="color:#000000"><strong>);</strong></span>target_pool_size <span style="color:#000000"><strong>=</strong></span> Math<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">max</span><span style="color:#000000"><strong>(</strong></span> min<span style="color:#000000"><strong>,</strong></span> target_pool_size <span style="color:#000000"><strong>-</strong></span> <span style="color:#009999">1</span> <span style="color:#000000"><strong>);</strong></span> <span style="color:#999988"><em>//expiring a resource resources the target size to match</em></span>removeResource<span style="color:#000000"><strong>(</strong></span> resc <span style="color:#000000"><strong>);</strong></span><span style="color:#000000"><strong>}</strong></span><span style="color:#000000"><strong>...</strong></span>
<span style="color:#000000"><strong>}</strong></span><span style="color:#000000"><strong>public</strong></span> Object <span style="color:#990000"><strong>checkoutResource</strong></span><span style="color:#000000"><strong>(</strong></span> <span style="color:#445588"><strong>long</strong></span> timeout <span style="color:#000000"><strong>){</strong></span>Object resc <span style="color:#000000"><strong>=</strong></span> prelimCheckoutResource<span style="color:#000000"><strong>(</strong></span> timeout <span style="color:#000000"><strong>);</strong></span><span style="color:#000000"><strong>...</strong></span>
<span style="color:#000000"><strong>}</strong></span><span style="color:#000000"><strong>private</strong></span> <span style="color:#000000"><strong>synchronized</strong></span> Object <span style="color:#990000"><strong>prelimCheckoutResource</strong></span><span style="color:#000000"><strong>(</strong></span> <span style="color:#445588"><strong>long</strong></span> timeout <span style="color:#000000"><strong>){</strong></span><span style="color:#000000"><strong>...</strong></span>Object  resc <span style="color:#000000"><strong>=</strong></span> unused<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">get</span><span style="color:#000000"><strong>(</strong></span><span style="color:#009999">0</span><span style="color:#000000"><strong>);</strong></span><span style="color:#000000"><strong>...</strong></span><span style="color:#000000"><strong>else</strong></span> <span style="color:#990000"><strong>if</strong></span> <span style="color:#000000"><strong>(</strong></span> shouldExpire<span style="color:#000000"><strong>(</strong></span> resc <span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>){</strong></span>removeResource<span style="color:#000000"><strong>(</strong></span> resc <span style="color:#000000"><strong>);</strong></span>ensureMinResources<span style="color:#000000"><strong>();</strong></span><span style="color:#000000"><strong>return</strong></span> <span style="color:#990000"><strong>prelimCheckoutResource</strong></span><span style="color:#000000"><strong>(</strong></span> timeout <span style="color:#000000"><strong>);</strong></span><span style="color:#000000"><strong>}</strong></span>
<span style="color:#000000"><strong>}</strong></span>
</code></span></span>

关注重点removeResource这个方法,这个是连接池管理连接的一个重要方法,大致浏览了下调用它的地方,注意到cullExpired状语从句:checkoutResource这两处:

  • cullExpired这个方法是C3P0的一个定时器里执行的方法,用来检查过期连接的,如果配置了相关的连接池参数(max_resource_age,MAX_IDLE_TIME,excess_max_idle_time,destroy_unreturned_resc_time),就会对过期连接进行清理;

    再结合应用的配置,maxIdleTime设置了1800秒,结合代码逻辑,c3p0默认会连接池过期检查 1800/4 = 450秒一次定时对idle连接进行,若连接过期(空闲了1800秒),则会被删除,被加入到formerResources中,如果最后剩下的空闲连接超过或小于minIdle的连接数,也会相应的进行缩减(收缩)或者扩充(爆炸)连接池的连接,达到minIdle个连接为止。

    有同学可能会问:“如果设置了minIdle和idleConnectionTestPeriod,这里的连接有保持活力,是否能逃过被清理的检查?”

    回答是:“无法逃过,idleConnectionTest是维持连接池的空闲连接和MySQL之间的心跳,防止MySQL服务器端踢掉应用的连接”,而前面提到的连接池过期检查则是c3p0对连接归还后是否长时间没有被再次借出为依据来判断连接是否已过期“。

    所以,即时是静静地呆着啥也不干,若配置了过期检查参数,这些keep alived的连接也会被认为过期而清理掉,然后重新生成一批新的,但啥也不干的时候,YGC也会很久一次,所以这些定期被清理掉的连接很可能熬不到老生代就被YGC回收了。

  • checkoutResource则是从连接池中获取连接的方法,代码从可以中看出每次连接电子杂志时,都会对该连接进行过期检查的校验,同样还是上面那些参数,比如配置了maxIdleTime是1800秒,检查时若发现连接上次借出归还后超过1800年没有再次被借出,则认为连接已过期,调用removeResource,将连接加入到formerResources中。

    最后,结合应用对的MySQL的调用场景:数据先从缓存(Redis的)中获取,若发生异常,则回源的MySQL优化版的改动增加了回源的MySQL的概率,所以,若是已经过期的连接,之前是定时450秒做一次检查,也就是可能会等450秒才被除去,然后再次生产新的连接;结果现在,可能没到450秒,而是通过结帐的同时就做了检查,加快了过期连接的失效和创建新连接的速度,导致formerResources里的”退休”连接变多,最终加快了老生代的增长。

    如何解决

    直接去掉maxIdleTime的配置,这个并不影响连接保活心跳,C3P0里对连接保活是在另外一个线程进行的,配置只要了idleConnectionTestPeriod这个参数即可。

    双11后的更新

    从这个具体的问题中跳出来,其实这是一类问题:持久化对象在JVM中的存活生命周期问题,连接池对象,本地缓存对象都是,这些对象存活时间久,处于JVM的老生代中,应用希望尽可能的重用它们,但若结合具体场景的配置或使用不合理,导致这些对象并未最大化被重用,比如上面提到的过期检查导致不断有新的对象被创建出来,因为是持久化对象,很容易就进入到了老生代,霸占了资源。

    同样的问题,JedisPool也存在,双11刚过,在流量高峰期,发现应用的老生代不断涨:

    图片

    很明显,在晚高峰20:00开始前,老生代是很安静的,从买买买开始,就不正常了,直到0点过后又恢复平静。

    一样的思路找下去,发现也是同样的问题,这是JedisPool默认的配置:

    <span style="color:#567482"><code><span style="color:#000000"><strong>public</strong></span> <span style="color:#000000"><strong>class</strong></span> <span style="color:#445588"><strong>JedisPoolConfig</strong></span> <span style="color:#000000"><strong>extends</strong></span> GenericObjectPoolConfig <span style="color:#000000"><strong>{</strong></span><span style="color:#000000"><strong>public</strong></span> <span style="color:#990000"><strong>JedisPoolConfig</strong></span><span style="color:#000000"><strong>()</strong></span> <span style="color:#000000"><strong>{</strong></span><span style="color:#999988"><em>// defaults to make your life with connection pool easier :)</em></span>setTestWhileIdle<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>true</strong></span><span style="color:#000000"><strong>);</strong></span>setMinEvictableIdleTimeMillis<span style="color:#000000"><strong>(</strong></span><span style="color:#009999">60000</span><span style="color:#000000"><strong>);</strong></span>setTimeBetweenEvictionRunsMillis<span style="color:#000000"><strong>(</strong></span><span style="color:#009999">30000</span><span style="color:#000000"><strong>);</strong></span>setNumTestsPerEvictionRun<span style="color:#000000"><strong>(-</strong></span><span style="color:#009999">1</span><span style="color:#000000"><strong>);</strong></span><span style="color:#000000"><strong>}</strong></span>
    <span style="color:#000000"><strong>}</strong></span>
    </code></span>

    每30秒进行一次扫描,如果发现有空闲超过60秒的连接,则进行清除,若少于minIdle个连接,则再创建新的。

    而清除掉的连接若未及时的被YGC掉,就会溜进老生代再看看这段时间YGC的频率:

    图片

    从20:00开始到0点,每分钟YGC 3-4次,而JVM配置的-XX:MaxTenuringThreshold=4,所以空闲的连接被清除掉后,再创建出来新的连接,到1分钟后的下次检查到过期期间,很容易熬过4次YGC,晋升到老生代。

    为什么高峰期还有空闲的连接,配置的minIdle = 8,然而Redis的的响应太快,1个连接就可以轻松处理> 1000QPS的请求量,再加上100个redis的实例,每个实例1个连接,即可处理10WQPS(若请求是均匀散落到redis的上),所以在高峰期,仍然有部分的连接是空闲的。

    ?怎么解决呢继续看代码:

    <span style="color:#567482"><code>	<span style="color:#3c5d5d"><strong>@Override</strong></span><span style="color:#000000"><strong>public</strong></span> <span style="color:#445588"><strong>boolean</strong></span> <span style="color:#990000"><strong>evict</strong></span><span style="color:#000000"><strong>(</strong></span>EvictionConfig config<span style="color:#000000"><strong>,</strong></span> PooledObject<span style="color:#000000"><strong><</strong></span>T<span style="color:#000000"><strong>></strong></span> underTest<span style="color:#000000"><strong>,</strong></span><span style="color:#445588"><strong>int</strong></span> idleCount<span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>{</strong></span><span style="color:#000000"><strong>if</strong></span> <span style="color:#000000"><strong>((</strong></span>config<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">getIdleSoftEvictTime</span><span style="color:#000000"><strong>()</strong></span> <span style="color:#000000"><strong><</strong></span> underTest<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">getIdleTimeMillis</span><span style="color:#000000"><strong>()</strong></span> <span style="color:#000000"><strong>&&</strong></span>config<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">getMinIdle</span><span style="color:#000000"><strong>()</strong></span> <span style="color:#000000"><strong><</strong></span> idleCount<span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>||</strong></span>config<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">getIdleEvictTime</span><span style="color:#000000"><strong>()</strong></span> <span style="color:#000000"><strong><</strong></span> underTest<span style="color:#000000"><strong>.</strong></span><span style="color:#008080">getIdleTimeMillis</span><span style="color:#000000"><strong>())</strong></span> <span style="color:#000000"><strong>{</strong></span><span style="color:#000000"><strong>return</strong></span> <span style="color:#000000"><strong>true</strong></span><span style="color:#000000"><strong>;</strong></span><span style="color:#000000"><strong>}</strong></span><span style="color:#000000"><strong>return</strong></span> <span style="color:#000000"><strong>false</strong></span><span style="color:#000000"><strong>;</strong></span><span style="color:#000000"><strong>}</strong></span>
    </code></span>

    在公共池过期的策略中发现minEvictableIdleTimeMills有一个孪生兄弟softMinEvictableIdleTimeMillis(即idleSoftEvictTime),如果闲置的时间超过这个配置值,且当前空闲的连接个数比minIdle要多,则进行清除。

    所以解决办法是配置minEvictableIdleTimeMills=-1,,softMinEvictableIdleTimeMillis=60000这样就不会对minIdle的连接进行清理,只有当连接数超过minIdle后,才进行清理工作。

    优化效果

    [优化版本] 12小时老生代变化趋势:

    图片

    [未优化版本] 12小时老生代变化趋势:

    图片

    除了老生代增长速度平缓了,YGC的停顿时间也有明显改善:

    [优化版本] gc.log:

    <span style="color:#567482"><code><span style="color:#009999">2017</span><span style="color:#000000"><strong>-</strong></span><span style="color:#009999">11</span><span style="color:#000000"><strong>-</strong></span><span style="color:#009999">16</span><span style="color:#990000"><strong>T09:</strong></span><span style="color:#009999">54</span><span style="color:#000000"><strong>:</strong></span><span style="color:#009999">04.202</span><span style="color:#000000"><strong>+</strong></span><span style="color:#009999">0800</span><span style="color:#000000"><strong>:</strong></span> <span style="color:#009999">68118.496</span><span style="color:#000000"><strong>:</strong></span> <span style="color:#000000"><strong>[</strong></span>GC <span style="color:#009999">68118.496</span><span style="color:#000000"><strong>:</strong></span> <span style="color:#000000"><strong>[</strong></span><span style="color:#990000"><strong>ParNew:</strong></span> <span style="color:#009999">1680716</span>K<span style="color:#000000"><strong>-></strong></span><span style="color:#009999">2934</span>K<span style="color:#000000"><strong>(</strong></span><span style="color:#009999">1887488</span>K<span style="color:#000000"><strong>),</strong></span> <span style="color:#009999">0.0074850</span> secs<span style="color:#000000"><strong>]</strong></span> <span style="color:#009999">1786721</span>K<span style="color:#000000"><strong>-></strong></span><span style="color:#009999">108960</span>K<span style="color:#000000"><strong>(</strong></span><span style="color:#009999">3984640</span>K<span style="color:#000000"><strong>),</strong></span> <span style="color:#009999">0.0077080</span> secs<span style="color:#000000"><strong>]</strong></span> <span style="color:#000000"><strong>[</strong></span><span style="color:#990000"><strong>Times:</strong></span> user<span style="color:#000000"><strong>=</strong></span><span style="color:#009999">0.07</span> sys<span style="color:#000000"><strong>=</strong></span><span style="color:#009999">0.00</span><span style="color:#000000"><strong>,</strong></span> real<span style="color:#000000"><strong>=</strong></span><span style="color:#009999">0.01</span> secs<span style="color:#000000"><strong>]</strong></span>
    </code></span>

    [未优化版本] gc.log:

    <span style="color:#567482"><code><span style="color:#009999">2017</span><span style="color:#000000"><strong>-</strong></span><span style="color:#009999">11</span><span style="color:#000000"><strong>-</strong></span><span style="color:#009999">16</span><span style="color:#990000"><strong>T09:</strong></span><span style="color:#009999">53</span><span style="color:#000000"><strong>:</strong></span><span style="color:#009999">26.996</span><span style="color:#000000"><strong>+</strong></span><span style="color:#009999">0800</span><span style="color:#000000"><strong>:</strong></span> <span style="color:#009999">67675.370</span><span style="color:#000000"><strong>:</strong></span> <span style="color:#000000"><strong>[</strong></span>GC <span style="color:#009999">67675.370</span><span style="color:#000000"><strong>:</strong></span> <span style="color:#000000"><strong>[</strong></span><span style="color:#990000"><strong>ParNew:</strong></span> <span style="color:#009999">1718298</span>K<span style="color:#000000"><strong>-></strong></span><span style="color:#009999">39709</span>K<span style="color:#000000"><strong>(</strong></span><span style="color:#009999">1887488</span>K<span style="color:#000000"><strong>),</strong></span> <span style="color:#009999">0.0267350</span> secs<span style="color:#000000"><strong>]</strong></span> <span style="color:#009999">1870355</span>K<span style="color:#000000"><strong>-></strong></span><span style="color:#009999">192401</span>K<span style="color:#000000"><strong>(</strong></span><span style="color:#009999">3984640</span>K<span style="color:#000000"><strong>),</strong></span> <span style="color:#009999">0.0269810</span> secs<span style="color:#000000"><strong>]</strong></span> <span style="color:#000000"><strong>[</strong></span><span style="color:#990000"><strong>Times:</strong></span> user<span style="color:#000000"><strong>=</strong></span><span style="color:#009999">0.40</span> sys<span style="color:#000000"><strong>=</strong></span><span style="color:#009999">0.00</span><span style="color:#000000"><strong>,</strong></span> real<span style="color:#000000"><strong>=</strong></span><span style="color:#009999">0.02</span> secs<span style="color:#000000"><strong>]</strong></span>
    </code></span>

    避免了JedisPool里minIdle中池化对象的不断创建/销毁动作,YGC完后剩余的对象少了很多,YGC的搬迁工作减轻不少,所以停顿时间也大幅缩短了。

转载:https://neway6655.github.io/gc/2017/10/28/%E8%AE%B0%E4%B8%80%E6%AC%A1JVM%E8%80%81%E7%94%9F%E4%BB%A3%E5%A2%9E%E9%95%BF%E8%BF%87%E5%BF%AB%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5.html?tdsourcetag=s_pctim_aiomsg

 

这篇关于记一次JVM老生代增长过快问题排查的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/163296

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu