盘的分?/strong>
盘的分配分成一下几个步骤(逻辑比较单,׃贴代码了Q:
- Ҏ传入的sizeQ在AA Tree中查扑ֈ一个可以容U传入size大小的Region节点Qƈ找到的Region的前size部分分配Z个新的Regionq返回?br />
查找逻辑在RegionSetcM实现(findҎ)Q它从root节点向下查找Q因为root节点的contiguous字段保存了整|的最大sizeQ因而先查root节点的contiguousQ如果size比root的Contiguous要大Q则抛异常,因ؓ整棵树中已经没有比传入的size要大的Region。然后层U遍历AA树,如果当前节点的size要比传入的size大或相等Q则扑ֈ以容纳传入size大小的Region节点Q以当前节点的size大小的前部分新创Z个Regionq回Q否则如果它的左子树的contiguous字段要比传入size大,则向左子树查找;否则如果它的叛_树的contiguous字段要比传入的size大,则向叛_树查找;否则Q抛出异常,因ؓ左右子树都找不到可以容纳size大小的Region?/li>
- 新创徏的Region实例mark成已l用(q个新创建的Region的start和AA树中某个Region节点的startgP而end大小则不一定一P?br />
因ؓq个新创建的Region实例是要从AA树中的某个节点分出部分空_因而首先要AA树中的那个节点从树中U除Q然后如果树中移除的节点的end值和新创建的Region的endgP则直接移除就可以了,否则Q要树U移除的节点剩余部分的重新创Z个Region插回树中。从代码的角度,首先以新创徏的Region的start值找到树中对应的RegionQRegion中接收Long作ؓ参数的compareҎ的存在以实现q种方式的查找)Q将其移除ƈq回U除后的RegionQremoveAndReturnҎQ在RegionSet中重新拷贝一个Region实例是ؓ了防止通过Rq回的Region改变树内部的状态,因ؓRegion即作Z个Node也作为payload存在Q同时也可以l接下来的插入提供新的Region节点Q,然后把将新创建的Region从原树中的Region中移除,q里的移除逻辑假设新创建的Region可以是原树中的Region的前部分、中间部分以及末位部分,作ؓ前部分和末位部分Q因为Region是新创徏的节点,因而直接更新当前节点即可,而如果是中间部分Q则前部分作为当前节点,而后部分作ؓ新节点返回。最后,如果原树中节点还剩余部分数据作ؓ新的I闲盘区添回到I闲盘树中。最后,查是否需要增加文件大,q里只需要更新文件大的字段卛_而不需要实际增加文件的大小Q因为文件在写入时会自动增加大小。Region中移除其中的部分Region代码如下Q?br />
protected Region remove(Region r) throws IllegalArgumentException {
if (r.start < this.start || r.end > this.end) {
throw new IllegalArgumentException("Ranges : Illegal value passed to remove : " + this + " remove called for : " + r);
}
if (this.start == r.start) {
this.start = r.end + 1;
updateContiguous();
return null;
} else if (this.end == r.end) {
this.end = r.start - 1;
updateContiguous();
return null;
} else {
Region newRegion = new Region(r.end + 1, this.end);
this.end = r.start - 1;
updateContiguous();
return newRegion;
}
}
- 最后将分配出来的Region实例q回QƈU录在DiskMarker中,在以后需要将盘中的数据重新d到内存中时用于定位该数据在磁盘中的位|,q可以将该Regin回收?/li>
盘的回?br />
盘的回收也分几个步骤来完成Q?br />
- 对要回收的RegionQ查扑֜当前树中是否有一个Region节点其start和要回收的Region?end - 1)值相{,如果有,则删除树中的原节点ƈq回Q合q这两个节点Q将合ƈ后的节点重新插入到树中。Region中合q的代码逻辑如下Q?br />
protected void merge(Region r) throws IllegalArgumentException {
if (this.start == r.end + 1) {
this.start = r.start;
} else if (this.end == r.start - 1) {
this.end = r.end;
} else {
throw new IllegalArgumentException("Ranges : Merge called on non contiguous values : [this]:" + this + " and " + r);
}
updateContiguous();
}
- 对要回收的RegionQ或合ƈ后的RegionQ,l箋查找当前树是否有一个Region节点Q其end和要回收的(或已合ƈ的)Region?start - 1)的值相{,如果有,则删除树中的原节点ƈq回Q合q这两个节点Q将合ƈ后的节点l箋插入树中?/li>
- 如果在树中找不到可以和回收的Region合ƈ的Region节点Q则只是要合ƈ的Regiond到树中?/li>
- 最后如果回收后数据文g可以减小Q更新数据文件大的字段Qƈ数据文件的~小Q从而保持数据文件处于尽量小的状态?br />
最后我写了一个简单的试E序Q对盘分配与释攑ց了一些随机的模拟Q以增加一些直观的感受(cMDiskStorageFactoryQ在创徏FileAllocationTreeӞl了Long.MAX_VALUE的初始|我这也给q个g为初始|从而保证基本上在所有情况下Q都能找C个合适的Region节点Q也是说FileAllocationTree不用来控制数据文件的大小Q数据文件的大小由其他逻辑来控Ӟq在后面会详l讲?Q?nbsp;
public class FileAllocationTreeTest {
public static void main(String[] args) {
final int count = 5;
Random random = new Random();
FileAllocationTree alloc = new FileAllocationTree(Long.MAX_VALUE, null);
List<Region> allocated = Lists.newArrayList();
for(int i = 0; i < count; i++) {
int size = random.nextInt(1000);
Region region = alloc.alloc(size);
System.out.println("new size: " + size + ", " + toString(region) + ", filesize: " + alloc.getFileSize() + ", allocator: " + toString(alloc));
allocated.add(region);
}
for(int i = 0; i < count; i++) {
int size = random.nextInt(1000);
Region region = alloc.alloc(size);
System.out.println("new size: " + size + ", " + toString(region) + ", filesize: " + alloc.getFileSize() + ", allocator: " + toString(alloc));
allocated.add(region);
region = allocated.get(random.nextInt(allocated.size()));
alloc.free(region);
allocated.remove(region);
System.out.println("Freed region: " + toString(region) + ", after file size: " + alloc.getFileSize() + ", allocator: " + toString(alloc));
}
}
private static String toString(FileAllocationTree alloc) {
StringBuilder builder = new StringBuilder("[");
for(Region region : alloc) {
builder.append(toString(region)).append(", ");
}
builder.replace(builder.length() - 2, builder.length() - 1, "]");
return builder.toString();
}
private static String toString(Region region) {
return "Regin(" + region.start() + ", " + region.end() + ")";
}
}
输出的随机结果如下:
new size: 397, Regin(0, 396), filesize: 397, allocator: [Regin(397, 9223372036854775806)]
new size: 175, Regin(397, 571), filesize: 572, allocator: [Regin(572, 9223372036854775806)]
new size: 210, Regin(572, 781), filesize: 782, allocator: [Regin(782, 9223372036854775806)]
new size: 11, Regin(782, 792), filesize: 793, allocator: [Regin(793, 9223372036854775806)]
new size: 432, Regin(793, 1224), filesize: 1225, allocator: [Regin(1225, 9223372036854775806)]
new size: 226, Regin(1225, 1450), filesize: 1451, allocator: [Regin(1451, 9223372036854775806)]
Freed region: Regin(572, 781), after file size: 1451, allocator: [Regin(572, 781), Regin(1451, 9223372036854775806)]
new size: 500, Regin(1451, 1950), filesize: 1951, allocator: [Regin(572, 781), Regin(1951, 9223372036854775806)]
Freed region: Regin(793, 1224), after file size: 1951, allocator: [Regin(572, 781), Regin(793, 1224), Regin(1951, 9223372036854775806)]
new size: 681, Regin(1951, 2631), filesize: 2632, allocator: [Regin(572, 781), Regin(793, 1224), Regin(2632, 9223372036854775806)]
Freed region: Regin(1951, 2631), after file size: 1951, allocator: [Regin(572, 781), Regin(793, 1224), Regin(1951, 9223372036854775806)]
new size: 23, Regin(793, 815), filesize: 1951, allocator: [Regin(572, 781), Regin(816, 1224), Regin(1951, 9223372036854775806)]
Freed region: Regin(0, 396), after file size: 1951, allocator: [Regin(0, 396), Regin(572, 781), Regin(816, 1224), Regin(1951, 9223372036854775806)]
new size: 109, Regin(816, 924), filesize: 1951, allocator: [Regin(0, 396), Regin(572, 781), Regin(925, 1224), Regin(1951, 9223372036854775806)]
Freed region: Regin(1225, 1450), after file size: 1951, allocator: [Regin(0, 396), Regin(572, 781), Regin(925, 1450), Regin(1951, 9223372036854775806)]

]]>