MapReduce源码注释-MapTask.MapOutputBuffer.Buffer
?
BlockBuffer.reset
protected synchronized void reset() throws IOException { // spillLock unnecessary; If spill wraps, then // bufindex < bufstart < bufend so contention is impossible // a stale value for bufstart does not affect correctness, since // we can only get false negatives that force the more conservative path // 写入key时,发生跨界现象. 即写入某个key时,缓冲区尾部剩余空间不足以容纳整个key值,因此需要将key值分开存储,其中一部分存到缓冲区末尾,->key上半部分① // 另一部分存到缓冲区头部->key下半部分②. 由于key是排序的关键字,需要保证连续性. 因此需要将跨界的key值重新存储到缓冲区的头部位置. // 发生跨界的key在缓冲区末尾的长度=bufvoid-bufmark. 其中bufmark为最后(上一次)写入的一个完整的key/value的结束位置 int headbytelen = bufvoid - bufmark; // 将尾部key插入到头部之后, bufvoid要设置为bufmark. 那么bufvoid开始,长度为headbytelen的就是key的尾部部分了 bufvoid = bufmark; // 缓冲区前半段有足够的空间容纳整个key值. 即将尾部的key插入到头部后, 不会使得这一个key超过bufstart if (bufindex + headbytelen < bufstart) { // bufindex为当前缓冲区的位置. 不管写入key或者value, bufindex都表示下一个可写的初始位置 // [0, bufindex]在调整之前的头部① -> [headbytelen, headbytelen+bufindex] 即将原先头部后移headbytelen用来准备第二次拷贝 System.arraycopy(kvbuffer, 0, kvbuffer, headbytelen, bufindex); // [bufvoid, bufvoid+headbytelen]调整之前的尾部② -> [0, headbytelen] 将尾部②插入到原先的头部① System.arraycopy(kvbuffer, bufvoid, kvbuffer, 0, headbytelen); bufindex += headbytelen; // bufindex也要跟随移动到原先头部的下一个位置 } else { // 缓冲区前半段没有足够的空间容纳整个key值. 在将key值移动到缓冲区开始位置时触发一次spill操作 byte[] keytmp = new byte[bufindex];// bufindex为在未调整之前的缓冲区头部 System.arraycopy(kvbuffer, 0, keytmp, 0, bufindex); // 将原先的头部②复制到临时缓冲区中 bufindex = 0; // 重置缓冲区 out.write(kvbuffer, bufmark, headbytelen);// 首先将[bufmark, bufmark+headbytelen]即尾部①写入输出流 out.write(keytmp);// 然后将缓冲区头部,即key的下半部分也写入输出流 } }
?