`
ddupnow
  • 浏览: 41495 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多

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

 

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

 

压测环境介绍:

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

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

  $ 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左右

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内存对象的占用情况

$ jmap -histo 进程号|head -n 20

 

     结果如下:

 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$Segment对象实例最多,且占用了大量的内存。当前JVM内存占用总量为155M,ConcurrentHashMap$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!

    /**
     * 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编译后的文件会发现如下代码:

      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页面里加入了

<%@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
分享到:
评论
9 楼 漂泊一剑客 2017-12-15  
楼主,你好,问个问题,照理说jmp中,对象最多的应该是 session才对,怎么统计出来的是ConcurrentHashMap.Segment 和 ConcurrentHashMap.HashEntry 这些对象,求解?
8 楼 漂泊一剑客 2017-03-22  
这次我看懂了
7 楼 biran1980 2014-08-14  
呵呵,我们没用jsp,也出现楼主这种情况了
6 楼 di1984HIT 2014-07-21  
写的很好啊。
5 楼 greatwqs 2014-07-17  
没有想到Tomcat维护session代价这么高! 
4 楼 blueyy617 2013-11-21  
如果在没有源码的情况下,可以通过修改tomcat或者JVM参数来解决这个问题么?
3 楼 gaoge_2000 2013-08-23  
写得非常好!
2 楼 liuqing9382 2013-02-01  
跟我碰到的问题一模一样!
1 楼 xy_z487 2012-08-27  

相关推荐

    JVM入门实战/arthas实战/垃圾回收算法/垃圾回收器/jvm内存模型分析

    本系列课程从JVM基础到高级实战,老师手把手教你如何进行JVM调优,思路清晰,没有废话,旨在挑战高薪。 课程亮点: 1、基于阿里arthas进行JVM调优; 2、基于代码排查OOM问题,拒绝空讲; 3、总结JVM通用的调优思路;...

    jvm内存分析工具mat

    MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的Java虚拟机(JVM)内存分析工具。它主要用于诊断Java应用程序的内存泄漏问题,帮助开发者理解内存消耗情况,优化内存配置,从而提升应用性能。MAT以其易用性和...

    mat(mac)---jvm内存分析工具

    MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的Java虚拟机(JVM)内存分析工具,尤其适用于Mac OS X平台。这款工具可以帮助开发者诊断和解决Java应用中的内存泄漏问题,提高应用性能。MAT通过深入分析堆转储...

    jvm 内存分析文档

    【Jvm 内存分析文档】 Java 虚拟机(JVM)是Java程序的核心运行环境,它负责管理和执行字节码。JVM内存管理主要包括内存结构、内存分配以及垃圾回收(GC)等方面。了解这些知识对于优化Java应用程序的性能至关重要...

    idea插件JVM内存工具JProfiler11

    IntelliJ IDEA(简称Idea)作为广受欢迎的Java集成开发环境,提供了一系列强大的工具来帮助开发者进行性能调优,其中就包括JProfiler11这款强大的JVM内存分析工具。本文将详细介绍如何使用Idea中的JProfiler11插件,...

    JVM内存状况查看方法和分析工具

    JConsole 是 JDK 5 及以上版本中自带的工具,可以图形化查看 JVM 中内存的变化状况。JConsole 中显示了 JVM 中很多的信息:内存、线程、类和 MBean 等,在打开 JConsole 的内存 Tab 页后,可看到 JVM 内存部分的运行...

    JVM内存溢出问题解析

    在 JVM 中,如果 98%的时间是用于 GC 且可用的 Heap size 不足 2%的时候将抛出内存溢出异常信息。Heap Size 最大不要超过可用物理内存的 80%,一般的要将 Xmx 和 Xms 设置相同避免每次 GC 后都要调整虚拟机堆的...

    JVM内存空间分配笔记

    ### JVM内存空间分配详解 #### 一、JVM内存模型概览 JVM(Java虚拟机)内存模型主要由以下几个部分组成:程序计数器、Java虚拟机栈、本地方法栈、Java堆以及方法区(在JDK 8之后称为元空间)。下面将对这几个部分...

    jvm内存分析工具mat安装包

    MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的JVM内存分析工具,尤其适用于诊断Java应用程序的内存泄漏问题。在Java开发过程中,内存溢出(Out Of Memory)问题常常会导致程序异常终止,而MAT就是解决这类...

    JVM 内存分析

    对于描述中提到的博客链接,由于无法直接访问,我们可以假设博主可能讨论了JVM内存的分配策略、垃圾收集算法(如Minor GC、Major GC、Full GC)、内存泄漏检测以及如何通过工具(如VisualVM、JProfiler)进行内存...

    Jvm堆栈dump文件分析

    1. **内存分析**:检查堆内存的分配情况,找出内存泄漏的可能源头。它可以显示对象的生命周期,帮助定位哪些对象被过度保留,以及哪些类加载器可能导致内存泄露。 2. **线程分析**:分析所有运行中的线程,包括它们...

    JVM 深入学习教程深入分析JVM教程!jvm 内存原型,优化等等

    3. 分布式缓存:使用如Redis等缓存系统,减轻JVM内存压力。 4. 使用G1垃圾收集器:G1能实现并行、并发且低延迟的垃圾收集,适合大型应用。 四、JVM调优工具 1. JVisualVM:提供内存分析、线程监控、CPU性能分析等...

    java中jvm内存分配相关资料总结整理

    - **内存分析**:通过内存分析工具如MAT(Memory Analyzer Tool)分析heap dump文件,了解内存占用情况。 6. **内存模型** - **Java内存模型(JMM)**:定义了线程之间如何共享和访问内存,确保并发编程中的可见...

    JVM内存管理白皮书

    在这份由Sun Microsystems公司出版的《JVM内存管理白皮书》中,我们可以找到关于Java虚拟机(JVM)内存管理的详细介绍和深入分析。这份文档对于想要深入了解JVM工作原理的读者来说是一份宝贵的学习资料。在这份...

    jvm内存监控工具使用

    通过合理利用 VisualVM 及其集成的命令行工具,开发者能够有效地监控和分析 JVM 的内存使用情况,及时发现并解决内存泄漏、GC 效率低下等问题。掌握这些工具的使用方法,对于任何 Java 开发者而言都是必备技能。

    开发测试环境Docker及JVM内存限制部署方案

    该文档描述了开发测试环境中Docker及JVM内存限制部署方案

    java获得jvm内存大小

    在Java编程语言中,了解和控制JVM(Java虚拟机)的内存管理是至关重要的,尤其是在性能调优、资源管理和避免内存泄漏等方面。本文将深入探讨如何在Java中获取JVM内存大小,包括堆内存的总量、最大值以及剩余空间,并...

    JAVA jvm DUMP 内存分析

    性能测试,线程的 dump 看到线程的 死锁,等待 运行状态

    (二)MATJVM 内存分析工具.MAT JVM 内存分析工具.MAT JVM 内存分析工具.

    MAT(Memory Analyzer Tool)是IBM开发的一款强大的Java内存分析工具,专用于诊断和解决Java应用程序中的内存泄漏问题。MAT JVM内存分析工具可以帮助开发者深入理解Java虚拟机(JVM)的内存管理机制,通过分析堆内存...

    ibm HeapAnalyzer JVM内存分析工具 ha457.jar下载

    IBM HeapAnalyzer是一款强大的Java虚拟机(JVM)内存分析工具,专为诊断和解决Java应用程序的内存泄漏问题而设计。这个工具能够帮助开发者深入理解Java应用程序的内存使用情况,从而优化性能并防止由于内存泄漏导致...

Global site tag (gtag.js) - Google Analytics