内存完全由Java ConcurrentHashMap使用(在Tomcat下)

时间:2010-10-18 12:29:58

标签: java

这是一个内存堆栈(用作缓存),它只包含一个静态ConcurrentHashMap(CHM)。

所有传入的HTTP请求数据都存储在此ConcurrentHashMap中。并且有一个异步调度程序进程从同一个ConcurrentHashMap获取数据,并在将它们存储到数据库后删除key.value。

此系统运行良好且流畅,但只是按照以下标准发现,内存已充分利用(2.5GB)并且所有CPU时间都用于执行GC:

-concurrent http命中1000 / s

- 在15分钟内保持相同的并发命中

asynch进程每次写入数据库时​​都会记录CHM的剩余大小。 CHM.size()保持在Min:300到Max:3500

附近

我认为此应用程序存在内存泄漏。所以我使用Eclipse MAT来查看堆转储。运行可疑报告后,我从MAT获得了这些评论:

  

由“org.apache.catalina.loader.StandardClassLoader @ 0x853f0280”加载的“org.apache.catalina.session.StandardManager”的一个实例占用2,135,429,456(94.76%)个字节。内存累积在由“”加载的“java.util.concurrent.ConcurrentHashMap $ Segment []”的一个实例中。

3,646,166 instances of java.util.concurrent.ConcurrentHashMap$Segment retain >= 2,135,429,456 bytes.

Length    # Objects      Shallow Heap      Retained Heap 
0         3,646,166      482,015,968       >= 2,135,429,456 

上面的长度0我把它翻译为CHM内的空长记录(每次我调用CHM.remove()方法)。它与数据库中的记录数一致,创建此转储时数据库中有3,646,166条记录

奇怪的情况是:如果我暂停压力测试,堆内存中的利用率将逐渐释放到25MB。这大约需要30-45分钟。我重新模拟了这个应用程序,曲线看起来类似于下面的VisualVM图: alt text

提出问题:

1)这看起来像是内存泄漏吗?

2)每次删除调用remove(Object key, Object value)以从CHM中删除<key:value>,该删除的对象是否会获得GC?

3)这与GC设置有关吗?我添加了以下GC参数但没有帮助:

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:GCTimeRatio=19

-XX:+PrintGCTimeStamps

-XX:ParallelGCThreads=6

-verbose:gc

4)非常感谢任何解决这个问题的想法! :)

5)是否可以,因为我的所有参考都是硬参考?我的理解是,只要HTTP会话结束,所有那些非静态的变量现在都可用于GC。

注意我尝试用ehcache 2.2.0替换CHM,但是我遇到了相同的OutOfMemoryException问题。我想ehcache也在使用ConcurrentHashMap。

服务器规范:

-Xeon Quad core,8个线程。

-4GB内存

-Windows 2008 R2

-Tomcat 6.0.29

5 个答案:

答案 0 :(得分:10)

这个问题让我错了7天!最后我发现了真正的问题!以下是我尝试但未能解决OutOfMemory异常的任务:

- 使用concurrenthashmap更改为ehcache。 (事实证明ehcache也使用ConcurrentHashMap)

- 更改所有对Soft Reference的硬引用

- 根据Dr. Heinz M. Kabutz

的建议,将AbstractMap与concurrnetHashMap一起使用

百万美元的问题真的是“为什么30-45分钟后,内存开始释放回堆池?”

实际的根本原因是因为还有其他东西仍然存在实际的变量会话,而罪魁祸首是tomcat中的http会话仍处于活动状态!因此,即使http会话已完成,但如果超时设置为30分钟,则tomcat会将会话信息保留30分钟,然后JVM才能进行GC会话。将超时设置更改为1分钟后,问题立即解决。

$tomcat_folder\conf\web.xml

<session-config>
    <session-timeout>1</session-timeout>
</session-config>

希望这会帮助那些有类似问题的人。

答案 1 :(得分:9)

我认为您正在使用太多 会话数据 一次不适合 在内存中 。试试这个:

  1. 编辑bin/setenv.sh或在Tomcat启动器上设置 JVM args 的任何地方:

    附加-Dorg.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true

    e.g。

    # Default Java options
    if [ -z "$JAVA_OPTS" ]; then
            JAVA_OPTS="-server -Djava.awt.headless=true -XX:MaxPermSize=384m -Xmx1024m -Dorg.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true"
    fi
    
  2. conf/context.xml添加此内容之前修改</Context>

    <Manager className="org.apache.catalina.session.PersistentManager"
            maxIdleBackup="60" maxIdleSwap="300">
        <Store className="org.apache.catalina.session.FileStore"/>
    </Manager>
    
  3. 重新启动Tomcat,您的问题应该消失,因为它会使用文件系统来存储您的会话。

    在我看来,设置session-timeout = 1是一个解决方法,它掩盖了问题的根源,并且在大多数实际需要足够大session-timeout的应用中无法使用。我们的(Bippo)个应用通常会有session-timeout 2880分钟,即2天。

    参考:Tomcat 7.0 Session Manager Configuration

答案 2 :(得分:3)

  

1)这看起来像是内存泄漏吗?

是的,如果应用程序继续将对象放在地图中并且从不删除它们,那么很可能是内存泄漏。

  

2)每次删除调用remove(Object key,Object value)从CHM中删除a,删除的对象是否得到GC?

如果没有对它们有引用的实时(运行)线程,则只能对对象进行垃圾回收。地图只是有一个对象引用的地方。可能仍有其他地方引用同一个对象。但是将对象保留在地图中会阻止它被垃圾收集。

  

3)这与GC设置有关吗?

没有;如果引用了一个对象,则不能进行垃圾回收;你如何调整垃圾收集器并不重要。

答案 3 :(得分:1)

当然,现在回答为时已晚,但仅限于通过搜索找到此问题的其他人。它可能很有用。

这两个链接非常有用 https://issues.apache.org/bugzilla/show_bug.cgi?id=50685
http://wiki.apache.org/tomcat/OutOfMemory

简而言之,在大多数情况下,它是一个错误的测试或测试软件。当某些自定义软件打开URL时,如果此软件无法管理http会话,则tomcat会为每个请求创建新会话。例如,可以使用简单的代码进行检查,这些代码可以添加到JSP中。

System.out.println("session id: " + session.getId());
System.out.println("session obj: " + session);
System.out.println("session getCreationTime: " + (new Date(session.getCreationTime())).toString());
System.out.println("session.getValueNames().length: " + session.getValueNames().length);

如果从负载测试的角度来看一个用户的会话ID是相同的,那么没问题,如果每个请求都生成新的会话ID,这意味着测试软件不能很好地管理会话,测试结果不代表负载来自真实用户。

对于某些应用程序session.getValueNames()。length也很重要,因为例如,当普通用户工作时,它保持不变,但是当负载测试软件执行相同操作时,它会增长。这也意味着,负载测试软件并不能很好地代表真正的工作负载。在我的情况下,session.getValueNames()。普通用户的长度约为100,但是10分钟后qwith负载测试软件大约为500,最后系统崩溃时出现相同的OutOfMemory错误,MAT显示相同:

org.apache.catalina.loader.StandardClassLoader @ 0x853f0280“占用2,135,429,456(94.76%)字节。

答案 4 :(得分:0)

如果您遇到此异常且使用的是Spring引导版本1.4.4 RELEASE或更低版本,请设置property&#34; server.session-timeout&#34;的值。在几分钟内,而不是他们的建议(秒),以便及时清理堆上的会话。 或者您可以使用EmbeddedServletContainerCustomizer的bean,但提供的值将在几分钟内设置。

示例(10分钟内的会话超时): server.session-timeout = 10(在属性文件中设置) container.setSessionTimeout(10,TimeUnit.SECONDS); (在EmbeddedServletContainerCustomizer中设置)