【问题标题】:Tomcat memory managementTomcat 内存管理
【发布时间】:2012-06-11 14:16:32
【问题描述】:

我运行的是Tomcat7,服务器相当强大,8 GB RAM 8-core。

我的问题是RES内存越来越高,直到服务器不再响应,甚至没有调用OnOutOfMemoryError。

Tomcat 配置:

-Xms1024M
-Xmx2048M
-XX:PermSize=256m
-XX:MaxPermSize=512m
-XX:+UseConcMarkSweepGC
-XX:OnOutOfMemoryError='/var/tomcat/conf/restart_tomcat.sh'

内存信息:

Memory:     Non heap memory = 106 Mb (Perm Gen, Code Cache),
Loaded classes = 14,055,
Garbage collection time = 47,608 ms,
Process cpu time = 4,296,860 ms,
Committed virtual memory = 6,910 Mb,
Free physical memory = 4,906 Mb,
Total physical memory = 8,192 Mb,
Free swap space = 26,079 Mb,
Total swap space = 26,079 Mb
Perm Gen memory:    88 Mb / 512 Mb    ++++++++++++
Free disk space:    89,341 Mb 

Tomcat 使用的内存与 top 命令相比看起来并没有那么高。

在尝试连接到 SMTP 服务器或尝试连接到 facebook 服务器时,我也收到了 java.net.SocketException: No buffer space available

我使用 Hibernate,带有 c3p0 连接池,配置如下:

        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://urldb/schema?autoReconnect=true</property>
        <property name="hibernate.connection.username">username</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
        <property name="hibernate.connection.password"></property>
        <property name="connection.characterEncoding">UTF-8</property>

        <property name="hibernate.c3p0.acquire_increment">1</property>
        <property name="hibernate.c3p0.idle_test_period">300</property>
        <property name="hibernate.c3p0.timeout">5000</property>
        <property name="hibernate.c3p0.max_size">50</property>
        <property name="hibernate.c3p0.min_size">1</property>
        <property name="hibernate.c3p0.max_statement">0</property>
        <property name="hibernate.c3p0.preferredTestQuery">select 1;</property>
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

我找不到任何东西...有人暗示我应该在哪里寻找吗?

谢谢!

[更新 1]堆转储:

HEAP HISTOGRAM :

class [C                                    269780  34210054
class [B                                    5600    33836661
class java.util.HashMap$Entry               221872  6212416
class [Ljava.util.HashMap$Entry;            23797   6032056
class java.lang.String                      271170  5423400
class org.hibernate.hql.ast.tree.Node       103588  4972224
class net.bull.javamelody.CounterRequest    28809   2996136
class org.hibernate.hql.ast.tree.IdentNode  23461   2205334
class java.lang.Class                       14677   2113488
class org.hibernate.hql.ast.tree.DotNode    13045   1852390
class [Ljava.lang.String;                   48506   1335600
class [Ljava.lang.Object;                   12997   1317016 


Instance Counts for All Classes (excluding platform) :

103588 instances of class org.hibernate.hql.ast.tree.Node
33366 instances of class antlr.ANTLRHashString
28809 instances of class net.bull.javamelody.CounterRequest
24436 instances of class org.apache.tomcat.util.buf.ByteChunk
23461 instances of class org.hibernate.hql.ast.tree.IdentNode
22781 instances of class org.apache.tomcat.util.buf.CharChunk
22331 instances of class org.apache.tomcat.util.buf.MessageBytes
13045 instances of class org.hibernate.hql.ast.tree.DotNode
10024 instances of class net.bull.javamelody.JRobin
9084 instances of class org.apache.catalina.loader.ResourceEntry
7931 instances of class org.hibernate.hql.ast.tree.SqlNode 

[更新 2] server.xml :

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               URIEncoding="UTF-8"
               maxThreads="150"
               minSpareThreads="25"
               maxSpareThreads="75"
               enableLookups="false"
               acceptCount="1024"
               server="unknown"
               address="public_ip"
    />

****[更新 3] 日志文件的输出:****

    2012-06-04 06:18:24,152 [http-bio-ip-8080-exec-3500] ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/api].[Jersey REST Service]- Servlet.ser
vice() for servlet [Jersey REST Service] in context with path [/socialapi] threw exception
java.net.SocketTimeoutException: Read timed out

    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:532)
    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:501)
    at org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:563)
    at org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:118)
    at org.apache.coyote.http11.AbstractInputBuffer.doRead(AbstractInputBuffer.java:326)
    at org.apache.coyote.Request.doRead(Request.java:422)

[更新 4] ServletContext

我在我的应用程序中使用ServletContextListener 来实例化控制器并使用event.getServletContext().setAttribute 保持引用。这些控制器加载配置和翻译(Perm 中的 88Mb)。

然后使用我使用的数据库:

SessionFactory sf = dbManager.getSessionFactory(DatabaseManager.DB_KEY_DEFAULT);
Session session = sf.openSession();
Transaction tx = null; 

try {
    tx = session.beginTransaction();

    //Do stuuf

    tx.commit();

} catch (Exception e){
    //Do something
} finally {
    session.close();
}
  1. 这可能是泄漏源吗?
  2. 为什么不使用手动事务/会话,你会怎么做?

【问题讨论】:

  • 当服务器停止响应时,您还应该获得一个线程转储(请参阅wiki.apache.org/tomcat/…)以查看您的 JVM 实际在做什么。您能否也从 server.xml 发布您的 配置,告诉我们您的 DBCP 中启用的任何“废弃连接”功能,并告诉我们您的 JVM 进程的文件句柄限制是多少?日志中的任何内容——尤其是 logs/catalina.out(或 stdout 所在的任何位置)?
  • 更新 2:server.xml。好的,谢谢,下次它阻塞时我会尝试 jstack !在我的 mysql 进程列表上,我可以看到一个费用等待连接,这没什么奇怪的。在日志中,我已将其放入更新 3
  • 您的应用程序使用了很多 HQL?休眠注释或xml? HQL 最终是静态的吗?
  • HQL 最终是静态的吗?您的应用程序需要多少个线程。你在使用池化吗?
  • HQL 最终静态?不,我正在使用 SessionFactory,处理会话并从会话创建查询并在事务中执行查询。由于它在 tomcat 上运行,因此它是每个请求的线程。我正在使用 C3P0 进行 Mysql 连接池,在调试日志中看起来不错。

标签: java hibernate tomcat tomcat7


【解决方案1】:

试试这个参数:

+XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=dump.log

也可以尝试使用较低的启动内存参数-Xms

然后您可以检查转储以查看问题是否是对象分配。

运行时尝试

jps

这将输出所有java进程,假设TomcatPID 4444:

jmap -dump:format=b,file=heapdump 4444

jhat heapdump

如果您在执行 jhat 时内存不足,请添加更多内存。从那里您可以检查应用程序的堆。

另一种方法是启用 Hibernate 统计信息以检查您是否正在检索更多对象。虽然看起来每小时进行一次完整的垃圾收集应该不是问题(在那里做得更好)。

-verbose:gc -Xloggc:/opt/tomcat/logs/gc.out -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

例如,使用 GCViewer 查看内存的每个空间(ternured、eden、survivor、perm)。

另一个方便的工具:

jstack 4444 > stack.txt

这将检索在 pid 为 4444 的 java 进程中运行的每个线程的完整堆栈跟踪。

请记住,如果您以 root 或其他用户身份启动 Tomcat,则需要特权。

jps

不会输出您没有权限的进程,因此您无法连接到它。

由于我不知道您的应用程序是关于什么的(因此我不知道它的要求)300 万个实例看起来很多。

使用Hibernate statistics,您可以查看您最常实例化哪些类。

然后调整eden and ternured 垃圾回收的比例会更有效。

新实例化的对象进入伊甸园。当它填满一个次要 gc 触发器时。未删除的内容将进入幸存者空间。当它填满时,它会变成 ternured。当 ternured 满时会产生 Full gc。

在这张图片中(这是不准确的),我将String 放在一边,这些文件成为实习生和内存映射文件(不在堆中)。看看您最常实例化哪些类。大量使用String 可能会导致快速填满烫发。

我猜你这样做了,但使用托管会话工厂,例如 Spring(如果在你的堆栈中),并避免手动管理事务和会话。

请记住,当没有对象引用对象时,对象会在 GC 中被删除。因此,只要在您的应用程序中可以访问某个对象,该对象就会保留。

如果您的 ServletContextListener 实例化控制器并存储在事件 getServletContext 中。确保之后完全删除引用,如果保留引用,则不会删除对象,因为它们仍然可以访问。

如果您管理自己的事务和会话(如果您不能使用框架,这很好),那么您必须处理代码维护和错误,例如 Spring-tx 已经解决和改进。

我个人会利用FOSS。但当然有时你不能扩大堆栈。

如果您使用Hibernate,我会看看Spring-ormSpring-tx 来管理事务和会话。也可以看看Hibernate patter Open Session In View

【讨论】:

  • 谢谢,我将添加它并检查堆转储(我从 javamelody 检查它,它看起来不错并且与图表相对应:堆类:4,367,实例:3,376,160,千字节:372,020)
  • 谢谢,因为我的服务器现在运行正常,jstack 提供“无用”信息,但如果脚本再次发生冻结,我会这样做
  • 但是如果hibernate泄漏了对象,它不会停留在应用程序的“已用内存”中吗(例如:org.hibernate.hql.ast.tree.Node 240,128 Instances 11MB)?跨度>
  • 我的意思不是休眠舔,而是你的应用程序检索或创建不必要的对象(想想收集懒惰)
  • 你认为那些不必要的对象会被泄露吗?我在每个函数之后关闭会话,所以理论上应该是不可能的?
【解决方案2】:

我还建议您下载 Visual VM 1.3.3,安装所有插件,并将其附加到 Tomcat PID,这样您就可以实时查看正在发生的事情。为什么要等待线程转储?它还会告诉你 CPU、线程、所有堆代、哪些对象消耗的内存最多等。

【讨论】:

  • 为此他需要添加启动参数。 jmap也按需转储。 jvisualvm 几乎一样好,无需下载。 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
  • 我已经尝试过YourKit(它看起来类似于Visual VM),不幸的是并没有帮助我解决问题(我只能在开发环境中使用它),有没有办法将它插入生产服务器?
  • 感谢 ssedano,但这意味着您将 JMX 端口作为公共开放,这是我无法想象的
  • 你可以随时丢弃该端口上的传出流量(如果你在本地工作)
  • 不幸的是,我的问题仅发生在具有真实流量的专用服务器上的生产环境中,具有测试流量的本地服务器不会重现问题
猜你喜欢
  • 1970-01-01
  • 2011-07-21
  • 2012-03-21
  • 1970-01-01
  • 2016-10-12
  • 2012-06-14
相关资源
最近更新 更多