内存不足 Tomcat(无法创建新的本地线程)

4

我的一个Tomcat服务器上部署的应用程序一直出现内存不足错误,并在报错后关闭了Tomcat。

我查看了日志文件,发现如下信息:

SEVERE: Error allocating socket processor
java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:597)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.start(JIoEndpoint.java:513)
    at org.apache.tomcat.util.net.JIoEndpoint.newWorkerThread(JIoEndpoint.java:744)
    at org.apache.tomcat.util.net.JIoEndpoint.createWorkerThread(JIoEndpoint.java:723)
    at org.apache.tomcat.util.net.JIoEndpoint.getWorkerThread(JIoEndpoint.java:757)
    at org.apache.tomcat.util.net.JIoEndpoint.processSocket(JIoEndpoint.java:789)
    at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:355)
    at java.lang.Thread.run(Thread.java:619)
18 Feb, 2015 5:43:30 PM org.apache.tomcat.util.net.JIoEndpoint createWorkerThread
INFO: Maximum number of threads (750) created for connector with address null and port 80

我正在使用这个连接器设置在server.xml文件中

<Connector port="80" protocol="HTTP/1.1" 
               connectionTimeout="10000" maxThreads="750" minSpareThreads="50" redirectPort="8443" />

有人能建议我该做什么吗?

线程转储

"http-80-123" daemon prio=6 tid=0x5f5e7400 nid=0xcfc runnable [0x619be000..0x619bf9e8]
   java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at com.microsoft.sqlserver.jdbc.DBComms.receive(Unknown Source)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(Unknown Source)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PreparedStatementExecutionRequest.executeStatement(Unknown Source)
    at com.microsoft.sqlserver.jdbc.CancelableRequest.execute(Unknown Source)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeRequest(Unknown Source)
    - locked <0x15c69398> (a com.microsoft.sqlserver.jdbc.TDSWriter)

我已经在20秒的时间内进行了4次线程转储,并发现这个tid始终保持活动状态

我们可以从这个转储中找到一些有用的信息吗?

我从线程转储中得到了这个信息,是否表示死锁条件"BLOCKED",因为我多次以相同的状态收到了80-90个提示。

"http-80-342" daemon prio=6 tid=0x5c0d7c00 nid=0x1d0c waiting for monitor entry [0x6ee0e000..0x6ee0fce8]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:247)
    at java.sql.DriverManager.getCallerClass(DriverManager.java:477)
    at java.sql.DriverManager.getConnection(DriverManager.java:576)
    at java.sql.DriverManager.getConnection(DriverManager.java:185)
    at t4u.common.DBConnection.getConnectionToDB(DBConnection.java:32)
    at t4u.functions.CommonFunctions.getProcessID(CommonFunctions.java:1465)
1个回答

3
这意味着您已经达到了JVM可以创建的线程数量限制。这可能是由操作系统施加的限制,也可能是由于资源不足(如内存)导致的。
如果是操作系统施加的限制,您可以尝试提高它,假设您有该系统的访问级别。
如果与内存有关,则需要增加可用内存(添加更多物理内存或增加VM上的内存),或降低每个线程的内存需求。为了做到后者,您可以使用JVM的-Xss参数更改线程堆栈大小。要找到理想值,请从非常低的值(如128K或256K)开始,并查看是否出现任何StackOverflow异常。如果出现,请增加直到它们消失。
另一个可能性是使用不同的连接器实现。您没有说您当前正在使用哪个,但 BIO(阻塞 IO)连接器每个请求消耗一个线程。根据您的应用程序正在做什么,如果使用 NIO 或 APR 连接器,则可能会看到更好的线程利用率(即处理相同数量的请求所需的线程更少)。这里没有足够的信息让我说出是这样还是那样,但您可能想要尝试一下您的应用程序,并查看所需的线程数是否减少。

INFO:为具有空地址和端口 80 的连接器创建的最大线程数(750)

这看起来像是您在 Tomcat 中的线程池上达到了限制。请检查conf/server.xml中的配置。如果您正在使用 Executor,请查找该元素上的maxThreads。如果没有,请查找 Connector 元素上的maxThreads


感谢您的回复,我已经编辑了问题,并且还提到了在server.xml中完成的连接器配置细节。 - Rohitesh
这很有帮助。你肯定达到了maxThreads的上限,它在你的连接器上设置为750。这不会对OOME有所帮助,事实上它可能会更容易发生,但增加maxThreads将允许您超过池中的750个线程。同样,除非您有足够的内存,否则这并没有什么帮助。请参阅我上面的建议来处理这个问题。 - Daniel Mikusa
哦,你也在使用BIO连接器。NIO或APR可能有助于减少所需的线程数量。具体减少多少取决于你的工作负载。如果有大量的IO操作,你会看到更多的好处;如果有大量的应用程序处理或CPU密集型任务,则可能不会有太大帮助。无论如何,如果你有一个测试环境,我建议尝试一下,看看会发生什么。 - Daniel Mikusa
所以您建议我使用 -Xss 参数更改 JVM 的线程堆栈大小。但是,在我的测试环境中,我没有遇到这个错误,所以我不知道该如何继续进行。在这种情况下,线程转储是否有帮助? - Rohitesh
我无法告诉你在这里会起作用什么,因为我不了解你的应用程序和环境足够多。我上面的建议是你可以尝试并可能有助于解决问题的事情。最终,你需要进一步调查和测试以找到解决方案。你有一个开发环境,这很好。尝试对该环境施加一些负载,看看是否可以复制该问题。如果失败,请尝试通过增加堆栈大小或降低开发环境中的内存来使问题更严重,这可能会更容易复制。然后你可以尝试上述解决方案。 - Daniel Mikusa
Daniel Mikusa,我已经发布了一个转储文件,指示了我在应用程序即将关闭时获取此线程转储的状态。 - Rohitesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接