工作之中写了个小的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
分享到:
相关推荐
本系列课程从JVM基础到高级实战,老师手把手教你如何进行JVM调优,思路清晰,没有废话,旨在挑战高薪。 课程亮点: 1、基于阿里arthas进行JVM调优; 2、基于代码排查OOM问题,拒绝空讲; 3、总结JVM通用的调优思路;...
MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的Java虚拟机(JVM)内存分析工具。它主要用于诊断Java应用程序的内存泄漏问题,帮助开发者理解内存消耗情况,优化内存配置,从而提升应用性能。MAT以其易用性和...
MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的Java虚拟机(JVM)内存分析工具,尤其适用于Mac OS X平台。这款工具可以帮助开发者诊断和解决Java应用中的内存泄漏问题,提高应用性能。MAT通过深入分析堆转储...
【Jvm 内存分析文档】 Java 虚拟机(JVM)是Java程序的核心运行环境,它负责管理和执行字节码。JVM内存管理主要包括内存结构、内存分配以及垃圾回收(GC)等方面。了解这些知识对于优化Java应用程序的性能至关重要...
IntelliJ IDEA(简称Idea)作为广受欢迎的Java集成开发环境,提供了一系列强大的工具来帮助开发者进行性能调优,其中就包括JProfiler11这款强大的JVM内存分析工具。本文将详细介绍如何使用Idea中的JProfiler11插件,...
JConsole 是 JDK 5 及以上版本中自带的工具,可以图形化查看 JVM 中内存的变化状况。JConsole 中显示了 JVM 中很多的信息:内存、线程、类和 MBean 等,在打开 JConsole 的内存 Tab 页后,可看到 JVM 内存部分的运行...
在 JVM 中,如果 98%的时间是用于 GC 且可用的 Heap size 不足 2%的时候将抛出内存溢出异常信息。Heap Size 最大不要超过可用物理内存的 80%,一般的要将 Xmx 和 Xms 设置相同避免每次 GC 后都要调整虚拟机堆的...
### JVM内存空间分配详解 #### 一、JVM内存模型概览 JVM(Java虚拟机)内存模型主要由以下几个部分组成:程序计数器、Java虚拟机栈、本地方法栈、Java堆以及方法区(在JDK 8之后称为元空间)。下面将对这几个部分...
MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的JVM内存分析工具,尤其适用于诊断Java应用程序的内存泄漏问题。在Java开发过程中,内存溢出(Out Of Memory)问题常常会导致程序异常终止,而MAT就是解决这类...
对于描述中提到的博客链接,由于无法直接访问,我们可以假设博主可能讨论了JVM内存的分配策略、垃圾收集算法(如Minor GC、Major GC、Full GC)、内存泄漏检测以及如何通过工具(如VisualVM、JProfiler)进行内存...
1. **内存分析**:检查堆内存的分配情况,找出内存泄漏的可能源头。它可以显示对象的生命周期,帮助定位哪些对象被过度保留,以及哪些类加载器可能导致内存泄露。 2. **线程分析**:分析所有运行中的线程,包括它们...
3. 分布式缓存:使用如Redis等缓存系统,减轻JVM内存压力。 4. 使用G1垃圾收集器:G1能实现并行、并发且低延迟的垃圾收集,适合大型应用。 四、JVM调优工具 1. JVisualVM:提供内存分析、线程监控、CPU性能分析等...
- **内存分析**:通过内存分析工具如MAT(Memory Analyzer Tool)分析heap dump文件,了解内存占用情况。 6. **内存模型** - **Java内存模型(JMM)**:定义了线程之间如何共享和访问内存,确保并发编程中的可见...
在这份由Sun Microsystems公司出版的《JVM内存管理白皮书》中,我们可以找到关于Java虚拟机(JVM)内存管理的详细介绍和深入分析。这份文档对于想要深入了解JVM工作原理的读者来说是一份宝贵的学习资料。在这份...
通过合理利用 VisualVM 及其集成的命令行工具,开发者能够有效地监控和分析 JVM 的内存使用情况,及时发现并解决内存泄漏、GC 效率低下等问题。掌握这些工具的使用方法,对于任何 Java 开发者而言都是必备技能。
该文档描述了开发测试环境中Docker及JVM内存限制部署方案
在Java编程语言中,了解和控制JVM(Java虚拟机)的内存管理是至关重要的,尤其是在性能调优、资源管理和避免内存泄漏等方面。本文将深入探讨如何在Java中获取JVM内存大小,包括堆内存的总量、最大值以及剩余空间,并...
性能测试,线程的 dump 看到线程的 死锁,等待 运行状态
MAT(Memory Analyzer Tool)是IBM开发的一款强大的Java内存分析工具,专用于诊断和解决Java应用程序中的内存泄漏问题。MAT JVM内存分析工具可以帮助开发者深入理解Java虚拟机(JVM)的内存管理机制,通过分析堆内存...
IBM HeapAnalyzer是一款强大的Java虚拟机(JVM)内存分析工具,专为诊断和解决Java应用程序的内存泄漏问题而设计。这个工具能够帮助开发者深入理解Java应用程序的内存使用情况,从而优化性能并防止由于内存泄漏导致...