final Object loadStub(int oid, Object obj, Class cls) {
		long pos = getPos(oid);

		if ((pos & (dbFreeHandleFlag | dbPageObjectFlag)) != 0) {
			throw new StorageError(StorageError.DELETED_OBJECT);
		}

		byte[] body = pool.get(pos & ~dbFlagsMask);
		ClassDescriptor desc;
		int typeOid = ObjectHeader.getType(body, 0);

		if (typeOid == 0) {
			desc = findClassDescriptor(cls);
		}
		else {
			desc = findClassDescriptor(typeOid);
		}

		// synchronized (objectCache)
		{
			if (obj == null) {
				obj = desc.customSerializable ? serializer.create(desc.cls)
						: desc.newInstance();
				objectCache.put(oid, obj);
			}

			assignOid(obj, oid, false);

			try {
				if (obj instanceof SelfSerializable) {
					((SelfSerializable) obj)
							.unpack(new ByteArrayObjectInputStream(body,
									ObjectHeader.sizeof, obj,
									recursiveLoading(obj), false));
				}
				else if (desc.customSerializable) {
					serializer.unpack(obj, new ByteArrayObjectInputStream(body,
							ObjectHeader.sizeof, obj, recursiveLoading(obj),
							false));
				}
				else {
					unpackObject(obj, desc, recursiveLoading(obj), body,
							ObjectHeader.sizeof, obj);
				}
			}
			catch (Exception x) {
				throw new StorageError(StorageError.ACCESS_VIOLATION, x);
			}
		}

		if (obj instanceof ILoadable) {
			((IPersistent) obj).onLoad();
		}

		if (listener != null) {
			listener.onObjectLoad(obj);
		}

		return obj;
	}