[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"