天天看點

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"