public/* synchronized */void backup(OutputStream out)
			throws java.io.IOException {
		if (!opened) {
			throw new StorageError(StorageError.STORAGE_NOT_OPENED);
		}
		synchronized (this) {
			objectCache.flush();
		}

		int curr = 1 - currIndex;
		final int nObjects = header.root[curr].indexUsed;
		long indexOffs = header.root[curr].index;
		int i, j, k;
		int nUsedIndexPages = (nObjects + dbHandlesPerPage - 1)
				/ dbHandlesPerPage;
		int nIndexPages = (int) ((header.root[curr].indexSize
				+ dbHandlesPerPage - 1) / dbHandlesPerPage);
		long totalRecordsSize = 0;
		long nPagedObjects = 0;
		int bitmapExtent = header.root[curr].bitmapExtent;
		final long[] index = new long[nObjects];
		final int[] oids = new int[nObjects];

		if (bitmapExtent == 0) {
			bitmapExtent = Integer.MAX_VALUE;
		}

		for (i = 0, j = 0; i < nUsedIndexPages; i++) {
			Page pg = pool.getPage(indexOffs + (long) i * Page.pageSize);

			for (k = 0; k < dbHandlesPerPage && j < nObjects; k++, j++) {
				long pos = Bytes.unpack8(pg.data, k * 8);
				index[j] = pos;
				oids[j] = j;

				if ((pos & dbFreeHandleFlag) == 0) {
					if ((pos & dbPageObjectFlag) != 0) {
						nPagedObjects += 1;
					}
					else if (pos != 0) {
						int offs = (int) pos & (Page.pageSize - 1);
						Page op = pool.getPage(pos - offs);
						int size = ObjectHeader.getSize(op.data, offs
								& ~dbFlagsMask);

						size = (size + dbAllocationQuantum - 1)
								& ~(dbAllocationQuantum - 1);
						totalRecordsSize += size;
						pool.unfix(op);
					}
				}
			}

			pool.unfix(pg);
		}

		Header newHeader = new Header();
		newHeader.curr = 0;
		newHeader.dirty = false;
		newHeader.databaseFormatVersion = header.databaseFormatVersion;

		long newFileSize = (long) (nPagedObjects + nIndexPages * 2 + 1)
				* Page.pageSize + totalRecordsSize;
		newFileSize = (newFileSize + Page.pageSize - 1) & ~(Page.pageSize - 1);
		newHeader.root = new RootPage[2];
		newHeader.root[0] = new RootPage();
		newHeader.root[1] = new RootPage();
		newHeader.root[0].size = newHeader.root[1].size = newFileSize;
		newHeader.root[0].index = newHeader.root[1].shadowIndex = Page.pageSize;
		newHeader.root[0].shadowIndex = newHeader.root[1].index = Page.pageSize
				+ (long) nIndexPages * Page.pageSize;
		newHeader.root[0].shadowIndexSize = newHeader.root[0].indexSize = newHeader.root[1].shadowIndexSize = newHeader.root[1].indexSize = nIndexPages
				* dbHandlesPerPage;
		newHeader.root[0].indexUsed = newHeader.root[1].indexUsed = nObjects;
		newHeader.root[0].freeList = newHeader.root[1].freeList = header.root[curr].freeList;
		newHeader.root[0].bitmapEnd = newHeader.root[1].bitmapEnd = header.root[curr].bitmapEnd;

		newHeader.root[0].rootObject = newHeader.root[1].rootObject = header.root[curr].rootObject;
		newHeader.root[0].classDescList = newHeader.root[1].classDescList = header.root[curr].classDescList;
		newHeader.root[0].bitmapExtent = newHeader.root[1].bitmapExtent = header.root[curr].bitmapExtent;

		byte[] page = new byte[Page.pageSize];
		newHeader.pack(page);
		out.write(page);

		long pageOffs = (long) (nIndexPages * 2 + 1) * Page.pageSize;
		long recOffs = (long) (nPagedObjects + nIndexPages * 2 + 1)
				* Page.pageSize;

		GenericSort.sort(new GenericSortArray() {
			public int size() {
				return nObjects;
			}

			public int compare(int i, int j) {
				return index[i] < index[j] ? -1 : index[i] == index[j] ? 0 : 1;
			}

			public void swap(int i, int j) {
				long t1 = index[i];
				index[i] = index[j];
				index[j] = t1;
				int t2 = oids[i];
				oids[i] = oids[j];
				oids[j] = t2;
			}
		});

		byte[] newIndex = new byte[nIndexPages * dbHandlesPerPage * 8];

		for (i = 0; i < nObjects; i++) {
			long pos = index[i];
			int oid = oids[i];

			if ((pos & dbFreeHandleFlag) == 0) {
				if ((pos & dbPageObjectFlag) != 0) {
					Bytes.pack8(newIndex, oid * 8, pageOffs | dbPageObjectFlag);
					pageOffs += Page.pageSize;
				}
				else if (pos != 0) {
					Bytes.pack8(newIndex, oid * 8, recOffs);
					int offs = (int) pos & (Page.pageSize - 1);
					Page op = pool.getPage(pos - offs);
					int size = ObjectHeader.getSize(op.data, offs
							& ~dbFlagsMask);

					size = (size + dbAllocationQuantum - 1)
							& ~(dbAllocationQuantum - 1);
					recOffs += size;
					pool.unfix(op);
				}
			}
			else {
				Bytes.pack8(newIndex, oid * 8, pos);
			}
		}

		out.write(newIndex);
		out.write(newIndex);

		for (i = 0; i < nObjects; i++) {
			long pos = index[i];

			if (((int) pos & (dbFreeHandleFlag | dbPageObjectFlag)) == dbPageObjectFlag) {
				if (oids[i] < dbBitmapId + dbBitmapPages
						|| (oids[i] >= bitmapExtent && oids[i] < bitmapExtent
								+ dbLargeBitmapPages - dbBitmapPages)) {
					int pageId = oids[i] < dbBitmapId + dbBitmapPages ? oids[i]
							- dbBitmapId : oids[i] - bitmapExtent
							+ bitmapExtentBase;
					long mappedSpace = (long) pageId * Page.pageSize * 8
							* dbAllocationQuantum;

					if (mappedSpace >= newFileSize) {
						Arrays.fill(page, (byte) 0);
					}
					else if (mappedSpace + Page.pageSize * 8
							* dbAllocationQuantum <= newFileSize) {
						Arrays.fill(page, (byte) -1);
					}
					else {
						int nBits = (int) ((newFileSize - mappedSpace) >> dbAllocationQuantumBits);
						Arrays.fill(page, 0, nBits >> 3, (byte) -1);
						page[nBits >> 3] = (byte) ((1 << (nBits & 7)) - 1);
						Arrays.fill(page, (nBits >> 3) + 1, Page.pageSize,
								(byte) 0);
					}

					out.write(page);
				}
				else {
					Page pg = pool.getPage(pos & ~dbFlagsMask);
					out.write(pg.data);
					pool.unfix(pg);
				}
			}
		}

		for (i = 0; i < nObjects; i++) {
			long pos = index[i];

			if (pos != 0
					&& ((int) pos & (dbFreeHandleFlag | dbPageObjectFlag)) == 0) {
				pos &= ~dbFlagsMask;
				int offs = (int) pos & (Page.pageSize - 1);
				Page pg = pool.getPage(pos - offs);
				int size = ObjectHeader.getSize(pg.data, offs);
				size = (size + dbAllocationQuantum - 1)
						& ~(dbAllocationQuantum - 1);

				while (true) {
					if (Page.pageSize - offs >= size) {
						out.write(pg.data, offs, size);
						break;
					}

					out.write(pg.data, offs, Page.pageSize - offs);
					size -= Page.pageSize - offs;
					pos += Page.pageSize - offs;
					offs = 0;
					pool.unfix(pg);
					pg = pool.getPage(pos);
				}

				pool.unfix(pg);
			}
		}

		if (recOffs != newFileSize) {
			Assert.that(newFileSize - recOffs < Page.pageSize);
			int align = (int) (newFileSize - recOffs);
			Arrays.fill(page, 0, align, (byte) 0);
			out.write(page, 0, align);
		}
	}