1

我有一个 springRest Web 服务端点,它返回一个大小为 4MB 的字符串文本。当我们对此端点进行负载测试时,我们经常看到堆峰值并最终导致系统崩溃。我在想——当我们提出请求时,每个请求都由一个线程单独服务。我的假设是:因为字符串保存在全局静态变量中,所以每个线程获取 4MB 的副本,并且在大约 3000 个请求之后,堆全部被消耗并且系统崩溃,因为每个 4MB 的 3000 个线程大约是 12GB,因此系统内存不足。但这是我的假设。

我的问题:在处理请求的每个线程完成它的工作后,tomcat 不会回收内存吗?这与GC(垃圾收集)有关吗?在请求生命周期中 - 随着请求的到来,创建了一个线程(根据该请求)该线程是获取它自己的响应副本还是仅引用响应?如果将那个巨大的字符串响应复制到每个线程,那么这可能就是显示堆峰值的原因。当响应返回给客户端时,tomcat 如何回收该线程的资源?它什么时候做?是否在声明与 GC 相关的请求线程?

我观察到的另一个方面是:socketWrite0() 方法的延迟——这需要 70-95% 的响应时间。我认为这是一个瓶颈。那么在请求响应的流程中——谁写入套接字?线程?还是线程将响应交给tomcat,然后由tomcat编写?

如果你们中的任何一个可以给我一个提示或一个方面来看看将内存峰值与巨大的字符串响应联系起来,我真的很感激。多谢你们!

玫瑰

4

3 回答 3

2

如果您声明了一个静态最终字符串,则该变量应在线程之间共享。没有任何代码,几乎不可能说出发生了什么。

您是否尝试过使用JProfilermat之类的分析工具?

于 2012-10-08T18:32:59.520 回答
2

我假设你所做的是

response.getWriter().write(hugeString);

如果这引起了问题,则应归咎于 tomcat,它可能从未想到过如此巨大的字符串。

您可以尝试将字符串切成更小的字符串,每次只写一个小字符串,例如

int N = hugeString.length();
int CHUNK = 8*1024;
for(int i=0; i<N; i+=CHUNK)
    int end = Math.min(i+CHUNK, N);
    writer.write( hugeString.substring(i, end) );
于 2012-10-08T18:33:34.733 回答
0

感谢大家给出的意见。这个问题非常神秘,因为它只有在系统受到多个用户的性能测试压力时才会出现。我们尝试对缓存客户端、缓存策略和缓存生存时间、连接超时进行不同的更改。

我们尝试改变算法。缓存更多数据以避免对遗留系统的昂贵调用。

我们尝试将数据结构从 JSONArray 更改为 String。

都没有帮助!!

最后我们发现是负载均衡器造成了问题。我们禁用了它,一切正常。

如果我有机会说一件事:企业系统的性能/可扩展性受到性能较差的组件的限制,因此请进行仔细的经验测试并在花时间找出正确的痛点后解决问题。不要跑来更改您的代码。

于 2012-10-24T01:29:16.530 回答