3

我们有两台以 Apache 为前端的 Tomcat 6.0.20 服务器,两者之间使用 AJP 进行通信。Tomcat 反过来使用 JBoss 集群上的 Web 服务。

今天早上,其中一台 Tomcat 机器在我们机器的 8 个内核中的 6 个上使用了 100% 的 CPU。我们使用 JConsole 进行堆转储,然后尝试连接 JVisualVM 以获取配置文件以查看占用所有 CPU 的原因,但这导致 Tomcat 崩溃。至少我们有堆转储!

我已将堆转储加载到 Eclipse MAT 中,在那里我发现我们有 565 个 java.lang.Thread 实例。其中一些显然是完全合法的,但绝大多数被命名为“ajp-6009-XXX”,其中 XXX 是一个数字。

我非常了解 Eclipse MAT 的使用方式,但无法找到解释。如果有人对为什么 Tomcat 可能会这样做有一些指示,或者有一些关于找出为什么使用 Eclipse MAT 的提示,那将不胜感激!

4

2 回答 2

1

我猜这不是一个直接的答案,但也许作为生产中的一种缓解方法,您可以通过限制配置中 AJP 的 maxThreads 来限制损害,根据http://tomcat.apache.org/tomcat-6.0-doc /config/ajp.html ?

默认值为 200,这肯定是很多线程 - 但这可能无法解释上面的 565。显然,这有可能将问题推到其他地方,但也许您最好能够在那里调试问题,或者它会以不同的方式表现出来。是否有可能您的负载量很大?在导致您遇到问题的期间,Apache 的行为有什么值得注意的吗?

于 2010-02-12T03:21:50.583 回答
0

除非您设法获得线程转储,否则无法确定,但是一旦我遇到了类似的问题,即所有 8 个内核都以 100% 的速度忙于数千个线程(但它不在 Tomcat 上)。

在我们的例子中,每个线程都被困java.util.HashMap在 get() 方法中,在 for 循环中紧紧地旋转:

   public V get(Object key) {
       if (key == null)
           return getForNullKey();
       int hash = hash(key.hashCode());
       for (Entry<K,V> e = table[indexFor(hash, table.length)];
            e != null;
            e = e.next) {
           Object k;
           if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
               return e.value;
       }
       return null;
   }

我们的理论是,特定存储桶中的条目链接列表不知何故已损坏并指向自身,因此永远无法退出循环。由于没有任何工作完成,随着请求的增加,越来越多的线程从池中被消耗。

如果在放置新条目时必须调整表的大小,但多个线程存在不受保护的读/写访问,则可能会发生这种情况;一个线程可能正在扩展特定存储桶位置的链表,而另一个线程正忙于尝试移动它。如果对哈希映射的访问不同步,那么它很可能会损坏(尽管通常不可重现)。

检查是否有多个线程可以同时访问的共享HashMap(或)。HashSet如果是这样,并且很容易做到这一点,要么用 a 替换ConcurrentHashMap,要么使用 aReentrantReadWriteLock来保护对地图的读/写访问。你当然也可以尝试Collections.synchronizedMap(),但这不会那么可扩展。

如果事实证明这是您的问题的根本原因,任何这些建议的修复程序都应该可以防止该问题。

也可以看看:

http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html

于 2010-04-05T20:01:17.707 回答