final long allocate(long size, int oid) {
		synchronized (objectCache) {
			setDirty();
			size = (size + dbAllocationQuantum - 1)
					& ~(dbAllocationQuantum - 1);
			Assert.that(size != 0);
			allocatedDelta += size;

			if (allocatedDelta > gcThreshold && !insideCloneBitmap) {
				gc0();
			}

			int objBitSize = (int) (size >> dbAllocationQuantumBits);
			Assert.that(objBitSize == (size >> dbAllocationQuantumBits));
			long pos;
			int holeBitSize = 0;
			int alignment = (int) size & (Page.pageSize - 1);
			int offs, firstPage, lastPage, i, j;
			int holeBeforeFreePage = 0;
			int freeBitmapPage = 0;
			int curr = 1 - currIndex;
			Page pg;

			lastPage = header.root[curr].bitmapEnd - dbBitmapId;
			usedSize += size;

			if (alignment == 0) {
				firstPage = currPBitmapPage;
				offs = (currPBitmapOffs + inc - 1) & ~(inc - 1);
			}
			else {
				firstPage = currRBitmapPage;
				offs = currRBitmapOffs;
			}

			while (true) {
				if (alignment == 0) {
					// allocate page object
					for (i = firstPage; i < lastPage; i++) {
						int spaceNeeded = objBitSize - holeBitSize < pageBits ? objBitSize
								- holeBitSize
								: pageBits;

						if (bitmapPageAvailableSpace[i] <= spaceNeeded) {
							holeBitSize = 0;
							offs = 0;
							continue;
						}

						pg = getBitmapPage(i);
						int startOffs = offs;

						while (offs < Page.pageSize) {
							if (pg.data[offs++] != 0) {
								offs = (offs + inc - 1) & ~(inc - 1);
								holeBitSize = 0;
							}
							else if ((holeBitSize += 8) == objBitSize) {
								pos = (((long) i * Page.pageSize + offs) * 8 - holeBitSize) << dbAllocationQuantumBits;

								if (wasReserved(pos, size)) {
									startOffs = offs = (offs + inc - 1)
											& ~(inc - 1);
									holeBitSize = 0;
									continue;
								}

								reserveLocation(pos, size);
								currPBitmapPage = i;
								currPBitmapOffs = offs;
								extend(pos + size);

								if (oid != 0) {
									long prev = getPos(oid);
									int marker = (int) prev & dbFlagsMask;
									pool.copy(pos, prev - marker, size);
									setPos(oid, pos | marker | dbModifiedFlag);
								}

								pool.unfix(pg);
								pg = putBitmapPage(i);

								int holeBytes = holeBitSize >> 3;

								if (holeBytes > offs) {
									memset(pg, 0, 0xFF, offs);
									holeBytes -= offs;
									pool.unfix(pg);
									pg = putBitmapPage(--i);
									offs = Page.pageSize;
								}

								while (holeBytes > Page.pageSize) {
									memset(pg, 0, 0xFF, Page.pageSize);
									holeBytes -= Page.pageSize;
									bitmapPageAvailableSpace[i] = 0;
									pool.unfix(pg);
									pg = putBitmapPage(--i);
								}

								memset(pg, offs - holeBytes, 0xFF, holeBytes);
								commitLocation();
								pool.unfix(pg);

								return pos;
							}
						}

						if (startOffs == 0 && holeBitSize == 0
								&& spaceNeeded < bitmapPageAvailableSpace[i]) {
							bitmapPageAvailableSpace[i] = spaceNeeded;
						}

						offs = 0;
						pool.unfix(pg);
					}
				}
				else {
					for (i = firstPage; i < lastPage; i++) {
						int spaceNeeded = objBitSize - holeBitSize < pageBits ? objBitSize
								- holeBitSize
								: pageBits;

						if (bitmapPageAvailableSpace[i] <= spaceNeeded) {
							holeBitSize = 0;
							offs = 0;
							continue;
						}

						pg = getBitmapPage(i);

						int startOffs = offs;

						while (offs < Page.pageSize) {
							int mask = pg.data[offs] & 0xFF;

							if (holeBitSize + Bitmap.firstHoleSize[mask] >= objBitSize) {
								pos = (((long) i * Page.pageSize + offs) * 8 - holeBitSize) << dbAllocationQuantumBits;

								if (wasReserved(pos, size)) {
									startOffs = offs += 1;
									holeBitSize = 0;
									continue;
								}

								reserveLocation(pos, size);
								currRBitmapPage = i;
								currRBitmapOffs = offs;
								extend(pos + size);

								if (oid != 0) {
									long prev = getPos(oid);
									int marker = (int) prev & dbFlagsMask;

									pool.copy(pos, prev - marker, size);
									setPos(oid, pos | marker | dbModifiedFlag);
								}

								pool.unfix(pg);
								pg = putBitmapPage(i);
								pg.data[offs] |= (byte) ((1 << (objBitSize - holeBitSize)) - 1);

								if (holeBitSize != 0) {
									if (holeBitSize > offs * 8) {
										memset(pg, 0, 0xFF, offs);
										holeBitSize -= offs * 8;
										pool.unfix(pg);
										pg = putBitmapPage(--i);
										offs = Page.pageSize;
									}

									while (holeBitSize > pageBits) {
										memset(pg, 0, 0xFF, Page.pageSize);
										holeBitSize -= pageBits;
										bitmapPageAvailableSpace[i] = 0;
										pool.unfix(pg);
										pg = putBitmapPage(--i);
									}

									while ((holeBitSize -= 8) > 0) {
										pg.data[--offs] = (byte) 0xFF;
									}

									pg.data[offs - 1] |= (byte) ~((1 << -holeBitSize) - 1);
								}

								pool.unfix(pg);
								commitLocation();

								return pos;
							}
							else if (Bitmap.maxHoleSize[mask] >= objBitSize) {
								int holeBitOffset = Bitmap.maxHoleOffset[mask];

								pos = (((long) i * Page.pageSize + offs) * 8 + holeBitOffset) << dbAllocationQuantumBits;

								if (wasReserved(pos, size)) {
									startOffs = offs += 1;
									holeBitSize = 0;
									continue;
								}

								reserveLocation(pos, size);
								currRBitmapPage = i;
								currRBitmapOffs = offs;
								extend(pos + size);

								if (oid != 0) {
									long prev = getPos(oid);
									int marker = (int) prev & dbFlagsMask;

									pool.copy(pos, prev - marker, size);
									setPos(oid, pos | marker | dbModifiedFlag);
								}

								pool.unfix(pg);
								pg = putBitmapPage(i);
								pg.data[offs] |= (byte) (((1 << objBitSize) - 1) << holeBitOffset);
								pool.unfix(pg);
								commitLocation();

								return pos;
							}

							offs += 1;

							if (Bitmap.lastHoleSize[mask] == 8) {
								holeBitSize += 8;
							}
							else {
								holeBitSize = Bitmap.lastHoleSize[mask];
							}
						}

						if (startOffs == 0 && holeBitSize == 0
								&& spaceNeeded < bitmapPageAvailableSpace[i]) {
							bitmapPageAvailableSpace[i] = spaceNeeded;
						}

						offs = 0;
						pool.unfix(pg);
					}
				}
				if (firstPage == 0) {
					if (freeBitmapPage > i) {
						i = freeBitmapPage;
						holeBitSize = holeBeforeFreePage;
					}

					objBitSize -= holeBitSize;
					// number of bits reserved for the object and aligned on
					// page boundary
					int skip = (objBitSize + Page.pageSize
							/ dbAllocationQuantum - 1)
							& ~(Page.pageSize / dbAllocationQuantum - 1);
					// page aligned position after allocated object
					pos = ((long) i << dbBitmapSegmentBits)
							+ ((long) skip << dbAllocationQuantumBits);

					long extension = (size > extensionQuantum) ? size
							: extensionQuantum;
					int oldIndexSize = 0;
					long oldIndex = 0;
					int morePages = (int) ((extension + Page.pageSize
							* (dbAllocationQuantum * 8 - 1) - 1) / (Page.pageSize * (dbAllocationQuantum * 8 - 1)));

					if (i + morePages > dbLargeBitmapPages) {
						throw new StorageError(StorageError.NOT_ENOUGH_SPACE);
					}

					if (i <= dbBitmapPages && i + morePages > dbBitmapPages) {
						// We are out of space mapped by memory default
						// allocation bitmap
						oldIndexSize = header.root[curr].indexSize;

						if (oldIndexSize <= currIndexSize + dbLargeBitmapPages
								- dbBitmapPages) {
							int newIndexSize = oldIndexSize;

							oldIndex = header.root[curr].index;

							do {
								newIndexSize <<= 1;

								if (newIndexSize < 0) {
									newIndexSize = Integer.MAX_VALUE
											& ~(dbHandlesPerPage - 1);

									if (newIndexSize < currIndexSize
											+ dbLargeBitmapPages
											- dbBitmapPages) {
										throw new StorageError(
												StorageError.NOT_ENOUGH_SPACE);
									}

									break;
								}
							}
							while (newIndexSize <= currIndexSize
									+ dbLargeBitmapPages - dbBitmapPages);

							if (size + newIndexSize * 8L > extensionQuantum) {
								extension = size + newIndexSize * 8L;
								morePages = (int) ((extension + Page.pageSize
										* (dbAllocationQuantum * 8 - 1) - 1) / (Page.pageSize * (dbAllocationQuantum * 8 - 1)));
							}

							extend(pos + (long) morePages * Page.pageSize
									+ newIndexSize * 8L);
							long newIndex = pos + (long) morePages
									* Page.pageSize;

							fillBitmap(
									pos
											+ (skip >> 3)
											+ (long) morePages
											* (Page.pageSize
													/ dbAllocationQuantum / 8),
									newIndexSize >>> dbAllocationQuantumBits);

							pool.copy(newIndex, oldIndex, oldIndexSize * 8L);
							header.root[curr].index = newIndex;
							header.root[curr].indexSize = newIndexSize;
						}

						int[] newBitmapPageAvailableSpace = new int[dbLargeBitmapPages];

						System.arraycopy(bitmapPageAvailableSpace, 0,
								newBitmapPageAvailableSpace, 0, dbBitmapPages);

						for (j = dbBitmapPages; j < dbLargeBitmapPages; j++) {
							newBitmapPageAvailableSpace[j] = Integer.MAX_VALUE;
						}

						bitmapPageAvailableSpace = newBitmapPageAvailableSpace;

						for (j = 0; j < dbLargeBitmapPages - dbBitmapPages; j++) {
							setPos(currIndexSize + j, dbFreeHandleFlag);
						}

						header.root[curr].bitmapExtent = currIndexSize;
						header.root[curr].indexUsed = currIndexSize += dbLargeBitmapPages
								- dbBitmapPages;
					}

					extend(pos + (long) morePages * Page.pageSize);

					long adr = pos;
					int len = objBitSize >> 3;

					// fill bitmap pages used for allocation of object space
					// with 0xFF
					while (len >= Page.pageSize) {
						pg = pool.putPage(adr);
						memset(pg, 0, 0xFF, Page.pageSize);
						pool.unfix(pg);
						adr += Page.pageSize;
						len -= Page.pageSize;
					}

					// fill part of last page responsible for allocation of
					// object space
					pg = pool.putPage(adr);
					memset(pg, 0, 0xFF, len);
					pg.data[len] = (byte) ((1 << (objBitSize & 7)) - 1);
					pool.unfix(pg);

					// mark in bitmap newly allocated object
					fillBitmap(pos + (skip >> 3), morePages
							* (Page.pageSize / dbAllocationQuantum / 8));

					j = i;

					while (--morePages >= 0) {
						setPos(getBitmapPageId(j++), pos | dbPageObjectFlag
								| dbModifiedFlag);
						pos += Page.pageSize;
					}

					header.root[curr].bitmapEnd = j + dbBitmapId;
					j = i + objBitSize / pageBits;

					if (alignment != 0) {
						currRBitmapPage = j;
						currRBitmapOffs = 0;
					}
					else {
						currPBitmapPage = j;
						currPBitmapOffs = 0;
					}

					while (j > i) {
						bitmapPageAvailableSpace[--j] = 0;
					}

					pos = ((long) i * Page.pageSize * 8 - holeBitSize) << dbAllocationQuantumBits;

					if (oid != 0) {
						long prev = getPos(oid);
						int marker = (int) prev & dbFlagsMask;

						pool.copy(pos, prev - marker, size);
						setPos(oid, pos | marker | dbModifiedFlag);
					}

					if (holeBitSize != 0) {
						reserveLocation(pos, size);

						while (holeBitSize > pageBits) {
							holeBitSize -= pageBits;
							pg = putBitmapPage(--i);
							memset(pg, 0, 0xFF, Page.pageSize);
							bitmapPageAvailableSpace[i] = 0;
							pool.unfix(pg);
						}

						pg = putBitmapPage(--i);
						offs = Page.pageSize;

						while ((holeBitSize -= 8) > 0) {
							pg.data[--offs] = (byte) 0xFF;
						}

						pg.data[offs - 1] |= (byte) ~((1 << -holeBitSize) - 1);
						pool.unfix(pg);
						commitLocation();
					}

					if (oldIndex != 0) {
						free(oldIndex, oldIndexSize * 8L);
					}

					return pos;
				}

				if (gcThreshold != Long.MAX_VALUE && !gcDone && !gcActive
						&& !insideCloneBitmap) {
					allocatedDelta -= size;
					usedSize -= size;
					gc0();
					currRBitmapPage = currPBitmapPage = 0;
					currRBitmapOffs = currPBitmapOffs = 0;
					return allocate(size, oid);
				}

				freeBitmapPage = i;
				holeBeforeFreePage = holeBitSize;
				holeBitSize = 0;
				lastPage = firstPage + 1;
				firstPage = 0;
				offs = 0;
			}
		}
	}