1.RecordFile
从RecordFile的commit方法部分代码:
 if (transactionsDisabled) {
                long offset = node.getBlockId() * BLOCK_SIZE;
                file.seek(offset);
                file.write(node.getData());
                node.setClean();
                free.add(node);
            }
可以断定,File被均匀(大小都为BLOCK_SIZE=8192)的分成N个BlockIo。每一个
BlockIo都有一个BlockId,BlockId是从0开始顺序递增的,这样只要知道这个BlockId
也就可以知道BlockIo的在RecordFile的位置了。
-----------------------------------------------------------------
|0   BlockIo                                                                               |8192
-----------------------------------------------------------------
|1   BlockIo                                                                               |8192
-----------------------------------------------------------------
|2   BlockIo                                                                               |8192
-----------------------------------------------------------------
.
.
.                                                                                               
-----------------------------------------------------------------
|n   BlockIo                                                                               |8192
-----------------------------------------------------------------

从getNewNode方法可以知道,BlockIo不是从RecordFile里面生成出来的,而是
直接new一个出来。因为RecordFile持有的是RandomAccessFile,即如果你要往里面
写数据你就直接写好了。先New一个BlockIo,往BlockIo里面填值以后,你要close或者
commit,那么就把BlockIo里面的数据写到这个RandomAccessFile里面就是了。
    private BlockIo getNewNode(long blockid)
    throws IOException
    {

        BlockIo retval = null;
        if (!free.isEmpty()) {
            retval = (BlockIo) free.removeFirst();
        }
        if (retval == null)
            retval = new BlockIo(0, new byte[BLOCK_SIZE]);

        retval.setBlockId(blockid);
        retval.setView(null);
        return retval;
    }

2.PageHeader
Pageheader对每一个(应该是除了第0个,第0个被FileHeader封装)BlockIo封装,PageHeader还维护
着前一个和后一个BlockIo,即通过PageHeader可以将所有的BlockIo串起来。被PageHeader封装的BlockIo
也就被称为一个Page

3.PageManager
PageManager实现了对Page的管理,包括对Page的allocate和free,获取第一个和最后一个Page,一个特定Page的前后Page,
以及RecordFile的commit和rollback。

4.PhysicalRow
PhysicalRowId,FreePhysicalRowId,FreePhysicalRowIdPage和FreePhysicalRowIdPageManager对BLockIo进行更细粒度的管理。每一个BlockIo被分成一个个PhysicalRowId(Id这个取名有歧义,其实就是BlockIo的一部分),
当然一个FreePhysicalRowId也可能跨越几个BlockIo。PhysicalRowId包含一个
Block Number和一个Offset,FreePhysicalRowId还有一个size来表示这块PhysicalRowId的size是多少。

FreePhysicalRowIdPage继承PageHeader,持有ELEMS_PER_PAGE(583)个FreePhysicalRowId供分配或者释放管理:

    // slots we returned.
    FreePhysicalRowId[] slots = new FreePhysicalRowId[ELEMS_PER_PAGE];

为了记录分配的个数,有一个setCount方法。和PageHeader将BlockIo封装起来并且维护前后的BlockIo不同,
FreePhysicalRowIdPage维护free的PhysicalRowId,这些Id是以一个数组组织起来的。相同的是都对BlockIo
进行封装:
 BlockIo curBlock = _file.get(freePage);
 FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
   .getFreePhysicalRowIdPageView(curBlock);
 int slot = fp.getFirstFree();
 if (slot != -1) {
  free = fp.alloc(slot); //这里调用了FreePhysicalRowIdPage的setCount操作,
           //这个count会写到BlockIo里面进行记录
     
  break;
 }
 
 _file.release(curBlock);

从这里可以看到:BlockIo除了被PageHeader封装外,还会被FreePhysicalRowIdPage封装,只是FreePhysicalRowIdPage
可能不会封装每一个BlockIo,只是free的这块而已。

这些FreePhysicalRowId拥有同样的BlockIo,offset具有一定规律:
   
    /** Returns the value of the indicated slot */
    FreePhysicalRowId get(int slot) {
 if (slots[slot] == null)
 slots[slot] = new FreePhysicalRowId(block, slotToOffset(slot)); //这时的offset只是区分,没有实际意思
 return slots[slot];
    }

    /** Converts slot to offset */
    short slotToOffset(int slot) {
 return (short) (O_FREE +
 (slot * FreePhysicalRowId.SIZE)); //即相差FreePhysicalRowId.SIZE,14个byte
    }

FreePhysicalRowId的使用大致是这样的:

 byte[] data = TestUtil.makeRecord(10000, (byte) 1);

 Location loc = physMgr.insert( data, 0, data.length );
 
 data = TestUtil.makeRecord(20000, (byte) 2);

 Location loc2 = physMgr.update(loc, data, 0, data.length );
 
 当更新的时候,数据量变大了,那么前面的10000byte容量放不下20000个,那么就释放掉10000这块容量:
 
 free( loc )具体代码如下:
 
 free( Location id )
        throws IOException
    {
        // get the rowid, and write a zero current size into it.
        BlockIo curBlock = file.get( id.getBlock() );
        DataPage curPage = DataPage.getDataPageView( curBlock );
        RecordHeader hdr = new RecordHeader( curBlock, id.getOffset() );
        hdr.setCurrentSize( 0 );
        file.release( id.getBlock(), true );

        // write the rowid to the free list
        freeman.put( id, hdr.getAvailableSize() );
    }
 最后调用的是FreePhysicalRowIdPageManager的put方法:
 
        /**
  * Puts the indicated rowid on the free list
  */
 void put(Location rowid, int size) throws IOException {

  FreePhysicalRowId free = null;
  PageCursor curs = new PageCursor(_pageman, Magic.FREEPHYSIDS_PAGE);
  long freePage = 0;
  while (curs.next() != 0) {
   freePage = curs.getCurrent();
   BlockIo curBlock = _file.get(freePage);
   FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
     .getFreePhysicalRowIdPageView(curBlock);
   int slot = fp.getFirstFree();
   if (slot != -1) {
    free = fp.alloc(slot);
    break;
   }

   _file.release(curBlock);
  }
  if (free == null) {
   // No more space on the free list, add a page.
   freePage = _pageman.allocate(Magic.FREEPHYSIDS_PAGE);
   BlockIo curBlock = _file.get(freePage);
   FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
     .getFreePhysicalRowIdPageView(curBlock);
   free = fp.alloc(0);
  }

  free.setBlock(rowid.getBlock());
  free.setOffset(rowid.getOffset());
  free.setSize(size);
  _file.release(freePage, true);
 }
   
    最后几行代码就是对FreePhysicalRowId对象进行了设置,以供后面使用,见PhysicalRowIdManager的alloc方法:
   
    alloc( int size )
        throws IOException
    {
        Location retval = freeman.get( size );
        if ( retval == null ) {
            retval = allocNew( size, pageman.getLast( Magic.USED_PAGE ) );
        }
        return retval;
    }
   
    FreePhysicalRowIdPageManager的get( size )方法有如下代码:
    int slot = fp.getFirstLargerThan(size)
    retval = new Location(fp.get(slot));  //fp.get(slot)返回一个FreePhysicalRowId,Location根据FreePhysicalRowId进行设置
    return retval;
   
    FreePhysicalRowIdPage的getFirstLargerThan(size):
        /**
  * Returns first slot with available size >= indicated size, or -1 if no
  * slots are available.
  **/
 int getFirstLargerThan(int size) {
  for (int i = 0; i < ELEMS_PER_PAGE; i++) {
   if (isAllocated(i) && get(i).getSize() >= size)  //getSize会取到前面的setSize的值
    return i;
  }
  return -1;
 }   
   
    Location的构造方法如下:
     /**
     * Creates a location based on the data of the physical rowid.
     */
    Location(PhysicalRowId src) {
        block = src.getBlock();
        offset = src.getOffset();
    }
   
FreePhysicalRowIdPageManager只有两个方法,是对FreePhysicalRowIdPage进行管理的。类似于
 /**
  * Returns a free physical rowid of the indicated size, or null if nothing
  * was found.
  */
 Location get(int size)
 
 /**
  * Puts the indicated rowid on the free list
  */
 void put(Location rowid, int size)