压力测试中JVM内存暴涨原因分析实战

压力测试中JVM内存暴涨原因分析实战 - day day up - ITeye技术网站 var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-535605-1']); _gaq.push(['_setDomainName', 'iteye.com']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
</div>

<div id="page">
  <div id="branding" class="clearfix">
    <div id="blog_name">
      <h1><a href="http://ddupnow.iteye.com/">day day up</a></h1>
    </div>
    <div id="fd"></div>
    <div id="blog_navbar">
      <ul>
        <li class="blog_navbar_for"><a href="http://ddupnow.iteye.com/"><strong>博客</strong></a></li>
        <li><a href="http://ddupnow.iteye.com/weibo">微博</a></li>
        <li><a href="http://ddupnow.iteye.com/album">相册</a></li>
        <li><a href="http://ddupnow.iteye.com/link">收藏</a></li>
        <li><a href="http://ddupnow.iteye.com/blog/guest_book">留言</a></li>
        <li><a href="http://ddupnow.iteye.com/blog/profile">关于我</a></li>
      </ul>

      <div class="search">
        <form action="/blog/search" method="get">
          <input class="search_text" id="query" name="query" style="margin-left: 10px; width: 110px;" type="text">
          <input class="submit_search" value="" type="submit">
        </form>
      </div> 
      <div id="fd"></div>         
    </div>
  </div>

  <div id="content" class="clearfix">
    <div id="main">
ddupnow

工作之中写了个小的Web应用,类似于脚手架的demo应用,用spring搭的,在tomcat里运行。

 

程序写完了,想看看能承受多大的访问压力,遂开始进行压力测试。没有用复杂的loadrunner压测,采用了一个简单的url压测工具http_load,只是单纯地用来压某个url的并发访问。

 

压测环境介绍:

      一台普通的linux服务器,双核,4G内存。

      tomcat启动内存和最大内存设置为1G,最大处理线程数为默认的200个,http_load模拟200个用户并发访问,持续10分钟

Shell代码   收藏代码
  1. &nbsp;http_load&nbsp;-p&nbsp;</span><span class="number">200</span><span>&nbsp;-s&nbsp;</span><span class="number">600</span><span>&nbsp;url&nbsp;&nbsp;</span></span></li></ol></div><pre title="压力测试中JVM内存暴涨原因分析实战" pre_index="0" source_url="http://ddupnow.iteye.com/blog/621619" codeable_type="Blog" codeable_id="621619" style="display: none;" class="shell" name="code"> http_load -p 200 -s 600 url

     

    使用lambdaprobe 监控tomcat的运行情况,发现jvm内存一直在上涨,从1%增长到90%,最后居然内存溢出了!才压了10分钟而已,感觉到非常奇怪。

     

    于是决定看看jvm的gc日志,在catalina.sh文件中加入jvm参数:

    -Xloggc:gclog.vgc -XX:+PrintGCTimeStamps -XX:-PrintGCDetails -XX:+UseConcMarkSweepGC

     

    观察输出的日志文件,发现将近每半秒进行一次GC操作,但是每秒内存还是在均匀上涨1M左右

    Gc日志片断代码   收藏代码
    1. 30.788: [GC 71240K->45204K(1045312K), 0.0673630 secs]  
    2. 31.519: [GC 71444K->45718K(1045312K), 0.0311260 secs]  
    3. 32.096: [GC 71958K->46210K(1045312K), 0.0425230 secs]  
    4. 32.714: [GC 72450K->46686K(1045312K), 0.0818290 secs]  
    5. 33.520: [GC 72926K->46970K(1045312K), 0.0288800 secs]  
    6. 34.757: [GC 73210K->47461K(1045312K), 0.0598320 secs]  
    7. 35.720: [GC 73701K->47802K(1045312K), 0.0303100 secs]  
    8. 36.576: [GC 74042K->48105K(1045312K), 0.0387580 secs]  
    9. 37.340: [GC 74345K->48541K(1045312K), 0.0298160 secs]  
    10. 38.099: [GC 74781K->49045K(1045312K), 0.0287100 secs]  
    11. 38.729: [GC 75285K->49463K(1045312K), 0.0271920 secs]  
    12. 39.428: [GC 75703K->49936K(1045312K), 0.0338770 secs]  
    13. 39.982: [GC 76176K->50422K(1045312K), 0.0341610 secs]  
    14. 40.444: [GC 76662K->50849K(1045312K), 0.0407040 secs]  
    15. 40.977: [GC 77089K->51304K(1045312K), 0.0308000 secs]  
    30.788: [GC 71240K->45204K(1045312K), 0.0673630 secs]
    31.519: [GC 71444K->45718K(1045312K), 0.0311260 secs]
    32.096: [GC 71958K->46210K(1045312K), 0.0425230 secs]
    32.714: [GC 72450K->46686K(1045312K), 0.0818290 secs]
    33.520: [GC 72926K->46970K(1045312K), 0.0288800 secs]
    34.757: [GC 73210K->47461K(1045312K), 0.0598320 secs]
    35.720: [GC 73701K->47802K(1045312K), 0.0303100 secs]
    36.576: [GC 74042K->48105K(1045312K), 0.0387580 secs]
    37.340: [GC 74345K->48541K(1045312K), 0.0298160 secs]
    38.099: [GC 74781K->49045K(1045312K), 0.0287100 secs]
    38.729: [GC 75285K->49463K(1045312K), 0.0271920 secs]
    39.428: [GC 75703K->49936K(1045312K), 0.0338770 secs]
    39.982: [GC 76176K->50422K(1045312K), 0.0341610 secs]
    40.444: [GC 76662K->50849K(1045312K), 0.0407040 secs]
    40.977: [GC 77089K->51304K(1045312K), 0.0308000 secs]

     

    于是想看看到底是哪些内存对象占用了过多的内存:

         首先找出tomcat进程ID,然后运行如下命令,获得其JVM内存对象的占用情况

    Shell代码   收藏代码
    1. &nbsp;jmap&nbsp;-histo&nbsp;进程号|head&nbsp;-n&nbsp;</span><span class="number">20</span><span>&nbsp;&nbsp;</span></span></li></ol></div><pre title="压力测试中JVM内存暴涨原因分析实战" pre_index="2" source_url="http://ddupnow.iteye.com/blog/621619" codeable_type="Blog" codeable_id="621619" style="display: none;" class="shell" name="code"> jmap -histo 进程号|head -n 20

       

           结果如下:

内存对象占用情况代码   收藏代码
  1.  num     #instances         #bytes  class name  
  2. ———————————————-  
  3.    1:       1040160       33285120  java.util.concurrent.ConcurrentHashMapSegment&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;<span class="number">2</span><span>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="number">1040354</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="number">24968496</span><span>&nbsp;&nbsp;java.util.concurrent.locks.ReentrantLockNonfairSync  
  4.    3:        192962       23240344  [C  
  5.    4:         32123       17892336  [B  
  6.    5:       1040287       17859608  [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;  
  7. ……  
  8. ……  
 num     #instances         #bytes  class name
----------------------------------------------
   1:       1040160       33285120  java.util.concurrent.ConcurrentHashMap$Segment
   2:       1040354       24968496  java.util.concurrent.locks.ReentrantLock$NonfairSync
   3:        192962       23240344  [C
   4:         32123       17892336  [B
   5:       1040287       17859608  [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;
......
......

 

可以看到,java.util.concurrent.ConcurrentHashMap SegmentJVM155MConcurrentHashMap Segment,ConcurrentHashMap$HashEntry,java.util.concurrent.locks.ReentrantLock 加起来占用了差不多一半的内存!~

 

回头查看自己的程序代码,看是否有使用ConcurrentHashMap对象,或者滥用该对象的情况。

 

检查后发现自己程序并没有使用ConcurrentHashMap对象!~很郁闷,到底是哪里在大量的对ConcurrentHashMap进行操作,使得该Map越来越大呢?

 

经过网上搜索,发现一个比jconsole还强大的jvm监控工具,也是sun推出的,叫做visualvm ,下载下来该工具后,还可以下载几个插件,丰富其监控功能,甚为好用。

附上一个监控图,非常详细和动态的显示了Eden,S0,S1,Old,Perm区的内存占用情况

 

由于要分析出到底哪里的ConcurrentHashMap对象被不断的put操作,使其异常庞大,于是首先用visualvm把heap dump出来。在Monitor标签页中有一个Heap Dump按钮,点击就是,该操作会把当前的jvm内存情况导出到一个文件中。

 

visualvm可以分析dump文件,但是我使用之后感觉非常慢。然后就是另一个工具登场了,HeapAnalyzer , 用该工具来分析dump出的文件。该工具下载下来后为jar文件,直接java -jar运行之。

 

找出size最大的java.util.concurrent.ConcurrentHashMap$Segment对象:



 查找出该对象的创建树结构:



发现该对象的父对象是org.apache.catalina.session.StandardManager.

查看tomcat源代码发现该类继承自ManagerBase,ManagerBase中果然有一个成员变量为ConcurrentHashMap!

Java代码   收藏代码
  1. /** 
  2.  * The set of currently active Sessions for this Manager, keyed by 
  3.  * session identifier. 
  4.  */  
  5. protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();  
    /**
     * The set of currently active Sessions for this Manager, keyed by
     * session identifier.
     */
    protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();

 

该对象存储的就是大名鼎鼎的容器session。

 

原来,http_load模拟的Url请求没有附带cookie和url重写,也就没有JSESSIONID信息,导致每一个请求,容器都会创建一个新的session。短短10分钟内200个线程并发访问,导致session暴涨。

 

但是我查看了自己的程序代码,并没有对session进行操作,那么容器是在何时创建新的session呢?

 

谜底就在于返回的JSP页面。学过jsp/servlet都知道,jsp页面默认有个session变量,查看tomcat目录下的jsp编译后的文件会发现如下代码:

Java代码   收藏代码
  1. response.setContentType(“text/html”);  
  2. pageContext = _jspxFactory.getPageContext(this, request, response,  
  3.           nulltrue8192true);  
  4. _jspx_page_context = pageContext;  
  5. application = pageContext.getServletContext();  
  6. config = pageContext.getServletConfig();  
  7. session = pageContext.getSession();  
  8. out = pageContext.getOut();  
  9. _jspx_out = out;  
      response.setContentType("text/html");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

getSession()方法会返回当前请求的JSESSIONID对应的session,如果没有就新创建一个session返回。

 

为了验证该问题,我在返回的JSP页面里加入了

Java代码   收藏代码
  1. <%@page session=“false”%>  
<%@page session="false"%>

 不使用session,再次进行压力测试,结果印证了我的想法。

 

下面是没加session=”false”和加上了的两种情况的内存增长图,作为对比,结果非常明显:


 

 

这次事件也告诉我不能随便选择测试工具,因为真实用户访问都是会附带cookie信息的,不会出现每个请求都需要创建session的情况。

 

附:

tomcat使用ConcurrentHashMap来存储session,在并发访问的情况下有更好的性能。有关更多session知识,可以参考:

     http://java.chinaitlab.com/base/802833.html

     http://xuehu2009.iteye.com/blog/524303

 

 

 

  • 大小: 107.2 KB
  • 大小: 51.3 KB
  • 大小: 42.1 KB
  • 大小: 11 KB
src="%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95%E4%B8%ADJVM%E5%86%85%E5%AD%98%E6%9A%B4%E6%B6%A8%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98_files/794.html" height="60" scrolling="no" width="468">
分享到:
评论
4 楼 blueyy617 2013-11-21  
如果在没有源码的情况下,可以通过修改tomcat或者JVM参数来解决这个问题么?
3 楼 gaoge_2000 2013-08-23  
写得非常好!
2 楼 liuqing9382 2013-02-01  
跟我碰到的问题一模一样!
1 楼 xy_z487 2012-08-27  


dp.SyntaxHighlighter.HighlightAll('code', true, true);
('#main .blog_content pre[name=code]').each(function(pre, index){ // blog content
    var post_id = 621619;
    var location = window.location;
    source_url = location.protocol + "//" + location.host + location.pathname + location.search;
    pre.writeAttribute('codeable_id', post_id);
    pre.writeAttribute('codeable_type', "Blog");
    pre.writeAttribute('source_url', source_url);
    pre.writeAttribute('pre_index', index);
    pre.writeAttribute('title', '压力测试中JVM内存暴涨原因分析实战');
  });

  fix_image_size(
('div.blog_content img'), 700); function processComment() {
('#main .blog_comment > div').each(function(comment){// comment
      var post_id = comment.id.substr(2);
("#"+comment.id+" pre[name=code]").each(function(pre, index){ var location = window.location; source_url = location.protocol + "//" + location.host + location.pathname + location.search; source_url += "#" + comment.id; pre.writeAttribute('codeable_id', post_id); pre.writeAttribute('codeable_type', "BlogComment"); pre.writeAttribute('source_url', source_url); pre.writeAttribute('pre_index', index); pre.writeAttribute('title', '压力测试中JVM内存暴涨原因分析实战'); }); }); } function quote_comment(id) { new Ajax.Request('/editor/quote', { parameters: {'id':id, 'type':'BlogComment'}, onSuccess:function(response){editor.bbcode_editor.textarea.insertAfterSelection(response.responseText); Element.scrollTo(editor.bbcode_editor.textarea.element);} }); } code_favorites_init(); processComment(); new WeiboShare({share_buttons: (shareweibo),imgscope: ('blog_content')});
    </div>

    <div id="local">
      <div class="local_top"></div>
      <div id="blog_owner">


ddupnow

      <div id="blog_actions">
        <ul>
          <li>浏览: 14599 次</li>
          <li>性别: <img alt="Icon_minigender_1" src="%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95%E4%B8%ADJVM%E5%86%85%E5%AD%98%E6%9A%B4%E6%B6%A8%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98_files/icon_minigender_1.gif" title="男"></li>
          <li>来自: 北京</li>
          <li><img src="%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95%E4%B8%ADJVM%E5%86%85%E5%AD%98%E6%9A%B4%E6%B6%A8%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98_files/offline.gif"></li>

        </ul>
      </div>
      <div id="user_visits" class="clearfix">
        <h5>最近访客 <span style="font-weight:normal;font-size:12px;padding-left:30px;"><a href="http://ddupnow.iteye.com/blog/user_visits">更多访客&gt;&gt;</a></span></h5>

          <div class="user_visit">
            <div class="logo"><a href="http://dylinshi126.iteye.com/" target="_blank"><img alt="dylinshi126的博客" class="logo" src="%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95%E4%B8%ADJVM%E5%86%85%E5%AD%98%E6%9A%B4%E6%B6%A8%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98_files/user-logo-thumb.gif" title="dylinshi126的博客: "></a></div>
            <div class="left"><a href="http://dylinshi126.iteye.com/" target="_blank" title="dylinshi126">dylinshi126</a></div>
          </div>

          <div class="user_visit">
            <div class="logo"><a href="http://nwujons.iteye.com/" target="_blank"><img alt="nwujons的博客" class="logo" src="%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95%E4%B8%ADJVM%E5%86%85%E5%AD%98%E6%9A%B4%E6%B6%A8%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98_files/00a2dafc-c993-3a4d-a5d1-a58a41f5c0fc-thumb.jpg" title="nwujons的博客: nwujons"></a></div>
            <div class="left"><a href="http://nwujons.iteye.com/" target="_blank" title="nwujons">nwujons</a></div>
          </div>

      </div>



                  <div id="blog_menu">
          <h5>文章分类</h5>
          <ul>
            <li><a href="http://ddupnow.iteye.com/">全部博客 (8)</a></li>

              <li><a href="http://ddupnow.iteye.com/category/91487">ubuntu linux (4)</a></li>

          </ul>
        </div>
        <div id="month_blogs">
          <h5>社区版块</h5>
          <ul>
            <li><a href="http://ddupnow.iteye.com/blog/news">我的资讯</a> (0)</li>
            <li>
              <a href="http://ddupnow.iteye.com/blog/post">我的论坛</a> (1)
            </li>
            <li><a href="http://ddupnow.iteye.com/blog/answered_problems">我的问答</a> (0)</li>
          </ul>
        </div>
        <div id="month_blogs">
          <h5>存档分类</h5>
          <ul>

              <li><a href="http://ddupnow.iteye.com/blog/monthblog/2010-03">2010-03</a> (3)</li>

              <li><a href="http://ddupnow.iteye.com/blog/monthblog/2010-01">2010-01</a> (5)</li>

            <li><a href="http://ddupnow.iteye.com/blog/monthblog_more">更多存档...</a></li>
          </ul>
        </div>



        <div id="guest_books">
          <h5>最新评论</h5>
          <ul>

            <li>
              <a href="http://blueyy617.iteye.com/" target="_blank" title="blueyy617">blueyy617</a>: 
              如果在没有源码的情况下,可以通过修改tomcat或者JVM参数 ...<br>
              <a href="http://ddupnow.iteye.com/blog/621619#bc2332031">压力测试中JVM内存暴涨原因分析实战</a>
            </li>

            <li>
              <a href="http://gaoge-2000.iteye.com/" target="_blank" title="gaoge_2000">gaoge_2000</a>: 
               写得非常好!<br>
              <a href="http://ddupnow.iteye.com/blog/621619#bc2323347">压力测试中JVM内存暴涨原因分析实战</a>
            </li>

            <li>
              <a href="http://liuqing9382.iteye.com/" target="_blank" title="liuqing9382">liuqing9382</a>: 
              跟我碰到的问题一模一样!<br>
              <a href="http://ddupnow.iteye.com/blog/621619#bc2299880">压力测试中JVM内存暴涨原因分析实战</a>
            </li>

            <li>
              <a href="http://xy-z487.iteye.com/" target="_blank" title="xy_z487">xy_z487</a>: 
              大<br>
              <a href="http://ddupnow.iteye.com/blog/621619#bc2278678">压力测试中JVM内存暴涨原因分析实战</a>
            </li>

          </ul>
        </div>

        <div class="local_bottom"></div>

    </div>
  </div>

  <div id="footer" class="clearfix">
    <div id="copyright">
      <hr>
      声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者。<br>
      © 2003-2014 ITeye.com.   All rights reserved.  [ 京ICP证110151号  京公网安备110105010620 ]
    </div>
  </div>
</div>
<script type="text/javascript">

document.write(“http://stat.iteye.com/?url=“+ encodeURIComponent(document.location.href) + “&referrer=” + encodeURIComponent(document.referrer) + “&user_id=’ width=’0’ height=’0’ />”);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值