我一直在努力寻找看起来非常基本的东西,这个问题与使用Jetty延续进行长期调查有关。
为了简单起见,我删除了所有特定于应用程序的代码,只留下了简单的延续相关代码。
我正在粘贴下面的servlet的doPost方法。我需要一些专家指导的关键问题是
在两种情况下我等待系统稳定,多次调用GC然后通过jConsole读取内存。它并不精确,但差异是如此之多和可解释的,这里或那里的精度只有几百字节无关紧要。
我的问题爆炸了,考虑到我的服务器需要保持100K连接,如果不是更多。在这里,这种无法计划的大小增加最终导致接近使用额外堆的GB。
(什么导致这个额外的堆使用,甚至从流中读取的内容都不会保留在doPost方法的范围之外。但它仍然会添加到堆中......我缺少什么?)
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Continuation cc = ContinuationSupport.getContinuation(req);
//if continuation is resumed, then send an answer back with
//hardcoded answer
if (cc.isResumed()) {
String myJson = "{\"1\",\"2\"}";
res.setContentType("application/json");
res.setContentLength(myJson.length());
PrintWriter writer = res.getWriter();
writer.write(myJson);
writer.close();
}
// if it is the first call to doPost ( not reentrant call )
else if (cc.isInitial()) {
//START :: decrease memory footprint :: comment this block :: START
// store the json from the request body in a string
StringBuffer jsonString = new StringBuffer();
String line = null;
BufferedReader bufferedReader = req.getReader();
while ((line = bufferedReader.readLine()) != null) {
jsonString.append(line);
}
//here jsonString was parsed and some values extracted
//though that code is removed for the sake of this publish
// as problem exists irrespective...of any processing
line = null;
bufferedReader.close();
bufferedReader = null;
jsonString = null;
// END :: decrease memory footprint :: comment this block :: END
cc.setTimeout(150000);
cc.suspend();
}
}
答案 0 :(得分:1)
造成这种额外堆使用的原因......
看看这一行:
BufferedReader bufferedReader = req.getReader();
请注意,您实际上并未创建新的BufferedReader。当您调用getBufferedReader
时,Jetty会创建一个BufferedReader,它包装一个InputStreamReader,它包装一个包装字节缓冲区的自定义InputStream实现。我很确定通过执行读取整个消息的代码,您可以在请求对象中创建大字节缓冲区,该缓冲区存储消息体的全部内容。此外,请求对象维护对读者的引用。
在您调用的函数的开头:
Continuation cc = ContinuationSupport.getContinuation(req);
我相信你的继续保留了存储所有数据的请求。因此,读取数据的简单行为是分配将保留的内存,直到您继续discontinue
为止。
有些事情你可以尝试作为一个实验。将您的代码更改为:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(req.getInputStream()));
这样Jetty就不会分配它自己的读者。再说一遍 - 我不知道与其他请求对象相比,读者中存储了多少数据 - 但它可能会有所帮助。
[更新]
另一种选择是避免这个问题。这就是我所做的(尽管我使用的是servlet 3.0而不是Continuations)。我有一个资源 - 让我们称之为/transfer
,这将POST一些数据,然后使用AsyncContext
等待响应。我将其更改为两个具有不同网址的请求 - /push
和/pull
。每当我有一些需要从客户端发送到服务器的内容时,它会进入/push
请求,然后在不创建AsyncContext
的情况下立即返回。因此,请求中的任何存储都会立即释放。然后等待响应,我发送了第二个GET请求,没有消息正文。当然 - 请求暂停了一段时间 - 但是谁在乎 - 它没有任何内容。
您可能需要重新考虑您的问题,并确定您是否可以分段执行任务 - 多个请求 - 或者您是否真的必须在单个请求中处理所有内容。