Memory leaks jetty延续和无法解释的内存使用模式

Memory leaks jetty延续和无法解释的内存使用模式,memory-leaks,jetty,continuations,Memory Leaks,Jetty,Continuations,我一直在努力解决一些看起来非常基本的问题,这个问题与长期使用Jetty continuations有关 为了简单起见,我删除了所有特定于应用程序的代码,只留下了简单的延续相关代码 我正在粘贴下面servlet的doPost方法。我需要一些专家指导的关键问题是 在下面的代码块中,如果我按原样运行并发出post请求,其中包含大约200字节的post正文,那么500个长轮询连接的内存量大约为20MB 如果我将突出显示的块注释为“减少内存占用::下面的注释块”,那么内存足迹将下降到7MB 在这两种情

我一直在努力解决一些看起来非常基本的问题,这个问题与长期使用Jetty continuations有关

为了简单起见,我删除了所有特定于应用程序的代码,只留下了简单的延续相关代码

我正在粘贴下面servlet的doPost方法。我需要一些专家指导的关键问题是

  • 在下面的代码块中,如果我按原样运行并发出post请求,其中包含大约200字节的post正文,那么500个长轮询连接的内存量大约为20MB
  • 如果我将突出显示的块注释为“减少内存占用::下面的注释块”,那么内存足迹将下降到7MB
在这两种情况下,我都会等待系统稳定,多次调用GC,然后通过jConsole读取内存。它并不精确,但差异是如此之大,而且可以解释,因此这里或那里的几个100字节的精度并不重要

考虑到我的服务器需要保持100K(如果不是更多的话)连接,我的问题爆发了。在这里,这种无法解释的大小增加最终导致使用接近GBs的额外堆

(当从流中读取的内容没有保留在doPost方法的范围之外时,是什么导致了这种额外的堆使用率。但它仍然会添加到堆中……我缺少什么?)

是什么导致了这种额外的堆使用

请看这一行:

BufferedReader bufferedReader = req.getReader();
请注意,您实际上并没有创建新的BufferedReader。调用
getBufferedReader
时,Jetty会创建一个BufferedReader,用于包装InputStreamReader,该读取器包装一个自定义InputStream实现,该实现包装一个字节缓冲区。我非常确定,通过执行读取整个消息的代码,您可以在请求对象内创建大字节缓冲区,该对象存储消息体的全部内容。另外,请求对象维护对读卡器的引用

在调用的函数开始时:

Continuation cc = ContinuationSupport.getContinuation(req);
我相信你的续集保留了存储所有数据的请求。因此,读取数据的简单操作就是分配内存,该内存将一直保留,直到您停止继续

有一件事你可以试着做个实验。将代码更改为:

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(req.getInputStream()));
这样Jetty就不会分配自己的读卡器。同样,我不知道与请求对象的其余部分相比,读卡器中实际存储了多少数据,但这可能会有所帮助

[更新]

另一种选择是避免这个问题。这就是我所做的(尽管我使用的是Servlet3.0,而不是Continuations)。我有一个资源,我们称之为
/transfer
,它将发布一些数据,然后使用
异步上下文
等待响应。我将其更改为两个具有不同URL的请求-
/push
/pull
。每当我有一些内容需要从客户端发送到服务器时,它就会进入
/push
请求,然后立即返回,而不创建
异步上下文。因此,请求中的任何存储都会立即释放。然后为了等待响应,我发送了第二个GET请求,没有消息正文。当然-请求会挂起一段时间-但谁在乎呢-它没有任何内容


您可能需要重新思考您的问题,并确定您是否可以分块执行任务—多个请求—或者您是否真的必须在一个请求中处理所有事情。

谢谢,我会尝试您的建议……与此同时,我尝试阅读ServletInputStream(与您指出的内容非常接近)这大大改善了情况。我不明白的是,当我从读卡器中读取数据并将其存储到方法范围内的字符串中时,为什么在清除continuation对象之前,对象一直保存在内存中。我所有字符串的作用域都是doPost…所以在doPost之后应该清理从流/输入或读取器读取的内容,除了我继续的内容。我知道我在某个地方错了…但不确定我在哪里接受了答案,尽管我继续了其他方式。我尝试了基于netty的我自己的服务器。在这种情况下,我可以在传入连接的读取结束后释放内存…所以我认为这是Jetty工作的方式…理想情况下,一旦有人暂停继续,他应该有一个选项,即完成请求选项,剩下的唯一事情就是发送响应。因此,应释放请求对象上的资源/缓冲区。(我会尝试一些特定于jetty的论坛,以获得该线程,尽管我正在为本Q的未来读者提供此文本示例)在jetty论坛上创建了一个主题,以防未来的读者想要跟踪…我添加的链接规范不完全由我的应用程序控制,在某些情况下,客户是第三方……因此拆分不是一个容易的选择(出于非技术原因)。我更相信使用Netty从头开始创建一个应用程序(因为我的测试显示,即使对于我持有的普通套接字,在jetty情况下作为延续对象,在Netty情况下作为通道,其内存消耗也会更好。大约13K Vs 4K字节)。我对servlet容器部分的需求不是很强烈,除了易于部署之外,我会找到一些解决办法……最有可能的是
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(req.getInputStream()));