提交 0fcdc214 authored 作者: Thomas Mueller's avatar Thomas Mueller

New experimental NIO storage.

上级 1af08c6d
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.store.fs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* File which uses NIO FileChannel.
*/
public class FileObjectDiskChannel implements FileObject {
private final String name;
private FileChannel channel;
FileObjectDiskChannel(String fileName, String mode) throws FileNotFoundException {
this.name = fileName;
RandomAccessFile raf = new RandomAccessFile(fileName, mode);
channel = raf.getChannel();
}
public void close() throws IOException {
channel.close();
}
public long getFilePointer() throws IOException {
return channel.position();
}
public String getName() {
return name;
}
public long length() throws IOException {
return channel.size();
}
public void readFully(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return;
}
if (channel.size() <= off + len) {
// TODO get size can degrade performance
throw new java.io.EOFException();
}
ByteBuffer buf = ByteBuffer.wrap(b);
buf.position(off);
buf.limit(off + len);
channel.read(buf);
}
public void seek(long pos) throws IOException {
// System.out.println("seek");
channel.position(pos);
}
public void setFileLength(long newLength) throws IOException {
// System.out.println("setFileLength");
// System.out.println(" "+channel.size()+" - "+channel.position());
if (newLength <= channel.size()) {
long oldPos = channel.position();
channel.truncate(newLength);
if (oldPos > newLength) {
oldPos = newLength;
}
channel.position(oldPos);
} else {
// extend by writting to new location
ByteBuffer b = ByteBuffer.allocate(1);
channel.write(b, newLength - 1);
}
// System.out.println(" "+channel.size()+" - "+channel.position());
}
public void sync() throws IOException {
// System.out.println("sync");
channel.force(true);
}
public void write(byte[] b, int off, int len) throws IOException {
// System.out.println("write");
ByteBuffer buf = ByteBuffer.wrap(b);
buf.position(off);
buf.limit(off + len);
channel.write(buf);
}
}
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel.MapMode;
import org.h2.constant.SysProperties;
import org.h2.util.FileUtils;
/**
* FileObject which is using NIO MappedByteBuffer mapped to memory from file.
*/
// TODO support files over 2 GB by using multiple buffers
// TODO howto dispose MappedByteBuffer?
public class FileObjectDiskMapped implements FileObject {
private static final long GC_TIMEOUT_MS = 10000;
private final String name;
private final MapMode mode;
private RandomAccessFile file;
private MappedByteBuffer mapped;
FileObjectDiskMapped(String fileName, String mode) throws IOException {
if ("r".equals(mode)) {
this.mode = MapMode.READ_ONLY;
} else {
this.mode = MapMode.READ_WRITE;
}
this.name = fileName;
file = new RandomAccessFile(fileName, mode);
remap();
}
private void unmap() {
if (mapped != null) {
// first write all data
mapped.force();
// need to dispose old direct buffer, see bug
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038
boolean useSystemGc;
if (SysProperties.NIO_CLEANER_HACK) {
try {
useSystemGc = false;
Method cleanerMethod = mapped.getClass().getMethod("cleaner");
cleanerMethod.setAccessible(true);
Object cleaner = cleanerMethod.invoke(mapped);
Method clearMethod = cleaner.getClass().getMethod("clear", new Class[0]);
clearMethod.invoke(cleaner, new Object[0]);
} catch (Throwable e) {
useSystemGc = true;
}
} else {
useSystemGc = true;
}
if (useSystemGc) {
WeakReference bufferWeakRef = new WeakReference(mapped);
mapped = null;
long start = System.currentTimeMillis();
while (bufferWeakRef.get() != null) {
if (System.currentTimeMillis() - start > GC_TIMEOUT_MS) {
throw new RuntimeException("Timeout (" + GC_TIMEOUT_MS
+ " ms) reached while trying to GC mapped buffer");
}
System.gc();
Thread.yield();
}
}
}
}
/**
* remap byte buffer into memory, called when file size has changed or file
* was created
*
* @throws IOException
*/
private void remap() throws IOException {
if (file.length() > Integer.MAX_VALUE) {
throw new RuntimeException("File over 2GB is not supported yet");
}
int oldPos = 0;
if (mapped != null) {
oldPos = mapped.position();
mapped.force();
unmap();
}
// maps new MappedByteBuffer, old one is disposed during GC
mapped = file.getChannel().map(mode, 0, file.length());
if (SysProperties.NIO_LOAD_MAPPED) {
mapped.load();
}
mapped.position(oldPos);
}
public void close() throws IOException {
unmap();
file.close();
file = null;
}
public long getFilePointer() throws IOException {
return mapped.position();
}
public String getName() {
return name;
}
public long length() throws IOException {
return file.length();
}
public void readFully(byte[] b, int off, int len) throws IOException {
mapped.get(b, off, len);
}
public void seek(long pos) throws IOException {
mapped.position((int) pos);
}
public void setFileLength(long newLength) throws IOException {
FileUtils.setLength(file, newLength);
remap();
}
public void sync() throws IOException {
file.getFD().sync();
mapped.force();
}
public void write(byte[] b, int off, int len) throws IOException {
// check if need to expand file
if (mapped.capacity() < mapped.position() + len) {
setFileLength(mapped.position() + len);
}
mapped.put(b, off, len);
}
}
......@@ -42,6 +42,16 @@ public abstract class FileSystem {
*/
public static final String PREFIX_SPLIT = "split:";
/**
* The prefix used for the NIO FileChannel file system.
*/
public static final String PREFIX_NIO = "nio:";
/**
* The prefix used for the NIO (memory mapped) file system.
*/
public static final String PREFIX_NIO_MAPPED = "nioMapped:";
/**
* Get the file system object.
*
......@@ -57,6 +67,10 @@ public abstract class FileSystem {
return FileSystemZip.getInstance();
} else if (fileName.startsWith(PREFIX_SPLIT)) {
return FileSystemSplit.getInstance();
} else if (fileName.startsWith(PREFIX_NIO)) {
return FileSystemDiskNio.getInstance();
} else if (fileName.startsWith(PREFIX_NIO_MAPPED)) {
return FileSystemDiskNioMapped.getInstance();
}
return FileSystemDisk.getInstance();
}
......
......@@ -47,7 +47,7 @@ public class FileSystemDisk extends FileSystem {
return new File(fileName).length();
}
private String translateFileName(String fileName) {
protected String translateFileName(String fileName) {
if (fileName != null && fileName.startsWith("~")) {
String userDir = SysProperties.USER_HOME;
fileName = userDir + fileName.substring(1);
......@@ -82,7 +82,7 @@ public class FileSystemDisk extends FileSystem {
throw Message.getSQLException(ErrorCode.FILE_RENAME_FAILED_2, new String[]{oldName, newName});
}
private void trace(String method, String fileName, Object o) {
protected void trace(String method, String fileName, Object o) {
if (SysProperties.TRACE_IO) {
System.out.println("FileSystem." + method + " " + fileName + " " + o);
}
......@@ -350,7 +350,7 @@ public class FileSystemDisk extends FileSystem {
return in;
}
private void freeMemoryAndFinalize() {
protected void freeMemoryAndFinalize() {
trace("freeMemoryAndFinalize", null, null);
Runtime rt = Runtime.getRuntime();
long mem = rt.freeMemory();
......
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class uses FileChannel.
*/
public class FileSystemDiskNio extends FileSystemDisk {
private static final FileSystemDiskNio INSTANCE = new FileSystemDiskNio();
public static FileSystemDisk getInstance() {
return INSTANCE;
}
public String createTempFile(String name, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
String file = super.createTempFile(name, suffix, deleteOnExit, inTempDir);
return FileSystem.PREFIX_NIO + file;
}
protected String translateFileName(String fileName) {
if (fileName.startsWith(FileSystem.PREFIX_NIO)) {
fileName = fileName.substring(FileSystem.PREFIX_NIO.length());
}
return super.translateFileName(fileName);
}
public InputStream openFileInputStream(String fileName) throws IOException {
return super.openFileInputStream(translateFileName(fileName));
}
public String normalize(String fileName) throws SQLException {
return FileSystem.PREFIX_NIO + super.normalize(fileName);
}
public String[] listFiles(String path) throws SQLException {
String[] list = super.listFiles(path);
for (int i = 0; list != null && i < list.length; i++) {
list[i] = FileSystem.PREFIX_NIO + list[i];
}
return list;
}
public String getParent(String fileName) {
return FileSystem.PREFIX_NIO + super.getParent(fileName);
}
public String getAbsolutePath(String fileName) {
return FileSystem.PREFIX_NIO + super.getAbsolutePath(fileName);
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
fileName = translateFileName(fileName);
FileObject f;
try {
f = new FileObjectDiskMapped(fileName, mode);
trace("openRandomAccessFile", fileName, f);
} catch (IOException e) {
freeMemoryAndFinalize();
try {
f = new FileObjectDiskMapped(fileName, mode);
} catch (IOException e2) {
e2.initCause(e);
throw e2;
}
}
return f;
}
protected FileObject open(String fileName, String mode) throws IOException {
return new FileObjectDiskChannel(fileName, mode);
}
}
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.store.fs;
import java.io.IOException;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class used memory mapped files.
*/
public class FileSystemDiskNioMapped extends FileSystemDisk {
private static final FileSystemDiskNioMapped INSTANCE = new FileSystemDiskNioMapped();
public static FileSystemDisk getInstance() {
return INSTANCE;
}
protected FileObject open(String fileName, String mode) throws IOException {
return new FileObjectDiskMapped(fileName, mode);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论