Java Netty 4.0 HTTP块内存泄漏?

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 下面是处理程序:

我正在尝试使用Netty 4.0使HTTP传输编码分块工作

到目前为止,我在这方面取得了成功。它适用于较小的有效载荷

然后我尝试使用大数据,它开始挂起

我怀疑我的代码可能有问题,或者ByteBuf.copy()可能有漏洞

我将代码精简到最低限度,以确保没有其他泄漏源或副作用,并最终编写了此测试

基本上,当您将wget连接到端口8888时,它会发送1GB的0x0。当我与他人联系时,我会重现这个问题

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决赛