天天看点

Kilim的小BUG

[java]

public bytebuffer fill(bytebuffer buf, int atleastn) throws ioexception, pausable {

if (buf.remaining() < atleastn) {

bytebuffer newbb = bytebuffer.allocate(math.max(buf.capacity() * 3 / 2, buf.position() + atleastn));

buf.rewind();

newbb.put(buf);

buf = newbb;

}

……

[/java]

后面的代码我省略了,这个bug就出现在这段代码里。这段代码的逻辑很简单,先是创建一个新的更大的缓冲区,然后将老的缓冲区的数据put到新的缓冲区,在put之前调用rewind方法将老的缓冲区的position设置为0。查看rewind干了什么:

public final buffer rewind() {

position = 0;

mark = -1;

return this;

仅仅是将position设置为0,并让mark失效。position指向下一个读或者写的位置,这里在写入到新缓冲区之前确实需要将position 设置为0,以便写入从老的缓冲区第一个位置开始。问题是什么?问题是position仅仅指定了下一个读取数据的位置,却没有指定有效数据的大小,换句话说,没有指定老的缓冲区的limit。因此这里造成的后果是老的缓冲区整个被写入到新的老缓冲区,包括有效数据和无效数据,默认情况下缓冲区的limit 等于capacity。

这个bug可以通过下面程序看出来:

bytebuffer old = bytebuffer.allocate(8);

old.putint(99);

bytebuffer newbuf = bytebuffer.allocate(16);

old.rewind();

newbuf.put(old);

newbuf.putint(100);

先往old写入一个整数99,然后创建newbuf并写入old数据,并再写入一个整数100,最后从newbuf读数据。本来我们预期只应该读到两个整数99和100,但是中间却插入一个0,输出如下:

12

99

100

public final buffer flip() {

limit = position;

修改上面的测试程序,符合我们的预期了:

old.flip();

输出:

8

while (buffer.hasremaining()) //发送数据

networkchannel.write(buffer);

buffer.rewind(); // 重置buffer,准备写入日志管道

while (buffer.hasremaining()) // 写入日志

loggerchannel.write(buffer);

而flip用于缓冲区发送或者读取之前,也就是将缓冲区设置为等待传出状态。

本文来源于"阿里中间件团队播客",原文发表时间"2010-11-03"