我有一个springRest Web服务端点,它返回一个大小为4MB的字符串文本。当我们对此端点进行负载测试时,我们不断看到堆峰值,最终系统崩溃。我在想 - 当我们发出请求时,每个请求都由一个线程单独提供服务。我的假设是:因为字符串保存在一个全局静态变量中,每个线程需要一个4MB的副本,大约3000个请求之后所有的消耗都被堆消耗,系统崩溃,因为3000个线程占用每个4MB大约12GB,因此系统失去记忆。但这是我的假设。
我的问题:在处理请求的每个线程完成它的工作后,tomcat是不是会回收内存? 这与GC(垃圾收集)有关吗? 在请求生命周期中 - 当请求到来时,创建一个线程(根据该请求)线程是否获得它自己的响应副本,或者它只引用响应?如果将这个巨大的字符串响应复制到每个线程,则可能就是堆尖峰显示的原因。 当响应返回给客户端时,tomcat如何回收该线程的资源?它什么时候做的?声称与GC有关的请求线程?
我观察到的另一个方面是:延迟方法socketWrite0() - 这需要70-95%的响应时间。我认为这是一个瓶颈。那么在请求响应流程中 - 谁写入套接字?线程?或者线程将响应交给tomcat并且tomcat写了吗?
如果你们中的任何一个人能够给我一个暗示或一个方面来看待与巨大的字符串响应相关的记忆峰值,我真的很感激。谢谢你们!
玫瑰
答案 0 :(得分:2)
答案 1 :(得分: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) );
答案 2 :(得分:0)
谢谢你们的评论。 这个问题非常神秘,因为它只有在系统受到多个用户的性能测试压力时才会出现。我们尝试了不同的更改来缓存客户端,缓存策略和缓存时间,以及连接超时。
我们尝试更改算法。缓存更多数据以避免对传统系统进行昂贵的调用。
我们尝试将数据结构从JSONArray更改为String。
一切都没有帮助!!
最后我们发现负载均衡器正在创建问题。我们禁用了它,一切正常。
如果我有机会说一件事:企业系统的性能/可扩展性受到性能较低的组件的影响,那么做一个仔细的实证测试并在花费时间找出正确的痛点后解决问题。不要改变你的代码。