Java Netty 4.0 HTTP块内存泄漏?
我正在尝试使用Netty 4.0使HTTP传输编码分块工作 到目前为止,我在这方面取得了成功。它适用于较小的有效载荷 然后我尝试使用大数据,它开始挂起 我怀疑我的代码可能有问题,或者ByteBuf.copy()可能有漏洞 我将代码精简到最低限度,以确保没有其他泄漏源或副作用,并最终编写了此测试 基本上,当您将wget连接到端口8888时,它会发送1GB的0x0。当我与他人联系时,我会重现这个问题Java Netty 4.0 HTTP块内存泄漏?,java,netty,Java,Netty,我正在尝试使用Netty 4.0使HTTP传输编码分块工作 到目前为止,我在这方面取得了成功。它适用于较小的有效载荷 然后我尝试使用大数据,它开始挂起 我怀疑我的代码可能有问题,或者ByteBuf.copy()可能有漏洞 我将代码精简到最低限度,以确保没有其他泄漏源或副作用,并最终编写了此测试 基本上,当您将wget连接到端口8888时,它会发送1GB的0x0。当我与他人联系时,我会重现这个问题 wget http://127.0.0.1:8888 -O /dev/null 下面是处理程序:
wget http://127.0.0.1:8888 -O /dev/null
下面是处理程序:
protected void channelRead0(ChannelHandlerContext ctx, FullHttpMessage msg) throws Exception {
DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
HttpHeaders.setTransferEncodingChunked(response);
response.headers().set(CONTENT_TYPE, "application/octet-stream");
ctx.write(response);
ByteBuf buf = Unpooled.buffer();
int GIGABYTE = (4 * 1024 * 1024); // multiply 256B = 1GB
for (int i = 0; i < GIGABYTE; i++) {
buf.writeBytes(CONTENT_256BYTES_ZEROED);
ctx.writeAndFlush(new DefaultHttpContent(buf.copy()));
buf.clear();
}
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
}
受保护的无效channelRead0(ChannelHandlerContext ctx,FullHttpMessage msg)引发异常{
DefaultHttpResponse响应=新的DefaultHttpResponse(HTTP_1_1,OK);
HttpHeaders.setTransferncodingChunked(响应);
response.headers().set(内容类型,“应用程序/八位字节流”);
写(应答);
ByteBuf buf=unmooled.buffer();
int GIGABYTE=(4*1024*1024);//乘以256B=1GB
对于(int i=0;i
我的方法有什么问题吗
编辑:
使用VisualVM,我发现ChannelOutboundBuffer
中存在内存泄漏
Entry[]buffer
不断增长,addCapacity()
被多次调用。条目
数组似乎包含已写入(或应写入)线路的缓冲区副本
我看到wireshark的数据进来了
这里有一个Dropbox链接,指向我已经发现我做错了什么
writeAndFlush()
的for
循环工作不正常,可能是泄漏的原因
我尝试了各种方法(参见gist链接中的许多修订)
我发现,要实现我想要做的事情而不出现内存泄漏,最好的方法是扩展InputStream并将InputStream写入上下文(而不是使用writeAndFlush()
),将InputStream封装在io.netty.handler.stream.ChunkedStream
中
DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
HttpHeaders.setTransferEncodingChunked(response);
response.headers().set(CONTENT_TYPE, "application/octet-stream");
ctx.write(response);
InputStream is = new InputStream() {
int offset = -1;
byte[] buffer = null;
@Override
public int read() throws IOException {
if (offset == -1 || (buffer != null && offset == buffer.length)) {
fillBuffer();
}
if (buffer == null || offset == -1) {
return -1;
}
while (offset < buffer.length) {
int b = buffer[offset];
offset++;
return b;
}
return -1;
}
// this method simulates an application that would write to
// the buffer.
// ONE GB (max size for the test;
int sz = 1024 * 1024 * 1024;
private void fillBuffer() {
offset = 0;
if (sz <= 0) { // LIMIT TO ONE GB
buffer = null;
return;
}
buffer = new byte[1024];
System.arraycopy(CONTENT_1KB_ZEROED, 0,
buffer, 0,
CONTENT_1KB_ZEROED.length);
sz -= 1024;
}
};
ctx.write(new ChunkedStream(new BufferedInputStream(is), 8192));
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
DefaultHttpResponse response=newdefaulthttpresponse(HTTP_1_1,OK);
HttpHeaders.setTransferncodingChunked(响应);
response.headers().set(内容类型,“应用程序/八位字节流”);
写(应答);
InputStream is=新的InputStream(){
整数偏移量=-1;
字节[]缓冲区=空;
@凌驾
public int read()引发IOException{
if(offset==1 | |(buffer!=null&&offset==buffer.length)){
fillBuffer();
}
如果(缓冲区==null | |偏移量==-1){
返回-1;
}
while(偏移量<缓冲区长度){
int b=缓冲区[偏移量];
offset++;
返回b;
}
返回-1;
}
//此方法模拟将写入的应用程序
//缓冲区。
//1 GB(测试的最大尺寸;
int sz=1024*1024*1024;
私有void fillBuffer(){
偏移量=0;
如果(sz)是4.0.8决赛