提交 202e687f authored 作者: Thomas Mueller's avatar Thomas Mueller

More accurate cache size calculation.

上级 1cab2114
...@@ -1597,7 +1597,7 @@ public class Database implements DataHandler { ...@@ -1597,7 +1597,7 @@ public class Database implements DataHandler {
} }
cacheSize = kb; cacheSize = kb;
if (pageStore != null) { if (pageStore != null) {
pageStore.getCache().setMaxSize(kb); pageStore.getCache().setMaxMemory(kb);
} }
} }
......
...@@ -230,7 +230,7 @@ public class UndoLogRecord { ...@@ -230,7 +230,7 @@ public class UndoLogRecord {
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
values[i] = buff.readValue(); values[i] = buff.readValue();
} }
row = new Row(values, 1); row = new Row(values, Row.MEMORY_CALCULATE);
row.setKey(key); row.setKey(key);
row.setDeleted(deleted); row.setDeleted(deleted);
row.setSessionId(sessionId); row.setSessionId(sessionId);
......
...@@ -70,10 +70,16 @@ public abstract class PageBtree extends Page { ...@@ -70,10 +70,16 @@ public abstract class PageBtree extends Page {
*/ */
protected boolean written; protected boolean written;
/**
* The estimated memory used by this object.
*/
protected int memoryEstimated;
PageBtree(PageBtreeIndex index, int pageId, Data data) { PageBtree(PageBtreeIndex index, int pageId, Data data) {
this.index = index; this.index = index;
this.data = data; this.data = data;
setPos(pageId); setPos(pageId);
memoryEstimated = index.getMemoryPerPage();
} }
/** /**
...@@ -260,9 +266,12 @@ public abstract class PageBtree extends Page { ...@@ -260,9 +266,12 @@ public abstract class PageBtree extends Page {
* *
* @return number of double words (4 bytes) * @return number of double words (4 bytes)
*/ */
public int getMemorySize() { public int getMemory() {
// four times the byte array size // need to always return the same value for the same object (otherwise
return index.getPageStore().getPageSize(); // the cache size would change after adding and then removing the same
// page from the cache) but index.getMemoryPerPage() can adopt according
// to how much memory a row needs on average
return memoryEstimated;
} }
public boolean canRemove() { public boolean canRemove() {
......
...@@ -8,6 +8,7 @@ package org.h2.index; ...@@ -8,6 +8,7 @@ package org.h2.index;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row; import org.h2.result.Row;
...@@ -32,6 +33,8 @@ public class PageBtreeIndex extends PageIndex { ...@@ -32,6 +33,8 @@ public class PageBtreeIndex extends PageIndex {
private RegularTable tableData; private RegularTable tableData;
private boolean needRebuild; private boolean needRebuild;
private long rowCount; private long rowCount;
private int memoryPerPage;
private int memoryCount;
public PageBtreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, public PageBtreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns,
IndexType indexType, boolean create, Session session) { IndexType indexType, boolean create, Session session) {
...@@ -424,4 +427,22 @@ public class PageBtreeIndex extends PageIndex { ...@@ -424,4 +427,22 @@ public class PageBtreeIndex extends PageIndex {
return row.getValue(columns[0].getColumnId()) != null; return row.getValue(columns[0].getColumnId()) != null;
} }
int getMemoryPerPage() {
return memoryPerPage;
}
/**
* The memory usage of a page was changed. The new value is used to adopt
* the average estimated memory size of a page.
*
* @param x the new memory size
*/
void memoryChange(int x) {
if (memoryCount < Constants.MEMORY_FACTOR) {
memoryPerPage += (x - memoryPerPage) / ++memoryCount;
} else {
memoryPerPage += (x > memoryPerPage ? 1 : -1) + ((x - memoryPerPage) / Constants.MEMORY_FACTOR);
}
}
} }
...@@ -8,6 +8,7 @@ package org.h2.index; ...@@ -8,6 +8,7 @@ package org.h2.index;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
...@@ -152,6 +153,7 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -152,6 +153,7 @@ public class PageBtreeLeaf extends PageBtree {
offsets = newOffsets; offsets = newOffsets;
rows = newRows; rows = newRows;
index.getPageStore().update(this); index.getPageStore().update(this);
memoryChange();
return -1; return -1;
} }
...@@ -177,6 +179,7 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -177,6 +179,7 @@ public class PageBtreeLeaf extends PageBtree {
start -= OFFSET_LENGTH; start -= OFFSET_LENGTH;
offsets = newOffsets; offsets = newOffsets;
rows = newRows; rows = newRows;
memoryChange();
} }
int getEntryCount() { int getEntryCount() {
...@@ -262,6 +265,7 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -262,6 +265,7 @@ public class PageBtreeLeaf extends PageBtree {
index.writeRow(data, offsets[i], rows[i], onlyPosition); index.writeRow(data, offsets[i], rows[i], onlyPosition);
} }
written = true; written = true;
memoryChange();
} }
void find(PageBtreeCursor cursor, SearchRow first, boolean bigger) { void find(PageBtreeCursor cursor, SearchRow first, boolean bigger) {
...@@ -339,4 +343,17 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -339,4 +343,17 @@ public class PageBtreeLeaf extends PageBtree {
store.free(getPos()); store.free(getPos());
} }
private void memoryChange() {
int memory = Constants.MEMORY_PAGE_BTREE + index.getPageStore().getPageSize();
if (rows != null) {
memory += getEntryCount() * (4 + Constants.MEMORY_POINTER);
for (SearchRow r : rows) {
if (r != null) {
memory += r.getMemory();
}
}
}
index.memoryChange(memory >> 2);
}
} }
...@@ -61,10 +61,16 @@ abstract class PageData extends Page { ...@@ -61,10 +61,16 @@ abstract class PageData extends Page {
*/ */
protected boolean written; protected boolean written;
/**
* The estimated memory used by this object.
*/
protected int memoryEstimated;
PageData(PageDataIndex index, int pageId, Data data) { PageData(PageDataIndex index, int pageId, Data data) {
this.index = index; this.index = index;
this.data = data; this.data = data;
setPos(pageId); setPos(pageId);
memoryEstimated = index.getMemoryPerPage();
} }
/** /**
...@@ -215,9 +221,12 @@ abstract class PageData extends Page { ...@@ -215,9 +221,12 @@ abstract class PageData extends Page {
* *
* @return number of double words (4 bytes) * @return number of double words (4 bytes)
*/ */
public int getMemorySize() { public int getMemory() {
// four times the byte array size // need to always return the same value for the same object (otherwise
return index.getPageStore().getPageSize(); // the cache size would change after adding and then removing the same
// page from the cache) but index.getMemoryPerPage() can adopt according
// to how much memory a row needs on average
return memoryEstimated;
} }
int getParentPageId() { int getParentPageId() {
...@@ -231,4 +240,6 @@ abstract class PageData extends Page { ...@@ -231,4 +240,6 @@ abstract class PageData extends Page {
return true; return true;
} }
} }
...@@ -45,7 +45,8 @@ public class PageDataIndex extends PageIndex { ...@@ -45,7 +45,8 @@ public class PageDataIndex extends PageIndex {
private HashMap<Integer, Integer> sessionRowCount; private HashMap<Integer, Integer> sessionRowCount;
private int mainIndexColumn = -1; private int mainIndexColumn = -1;
private DbException fastDuplicateKeyException; private DbException fastDuplicateKeyException;
private int memorySizePerPage; private int memoryPerPage;
private int memoryCount;
public PageDataIndex(RegularTable table, int id, IndexColumn[] columns, IndexType indexType, boolean create, Session session) { public PageDataIndex(RegularTable table, int id, IndexColumn[] columns, IndexType indexType, boolean create, Session session) {
initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType); initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType);
...@@ -79,10 +80,8 @@ public class PageDataIndex extends PageIndex { ...@@ -79,10 +80,8 @@ public class PageDataIndex extends PageIndex {
table.setRowCount(rowCount); table.setRowCount(rowCount);
// estimate the memory usage as follows: // estimate the memory usage as follows:
// the less column, the more memory is required, // the less column, the more memory is required,
// because the more rows fit on a page // because the more rows fit in a page
memorySizePerPage = store.getPageSize(); memoryPerPage = (Constants.MEMORY_PAGE_DATA + store.getPageSize()) >> 2;
int estimatedRowsPerPage = store.getPageSize() / ((1 + columns.length) * 8);
memorySizePerPage += estimatedRowsPerPage * 64;
} }
public DbException getDuplicateKeyException() { public DbException getDuplicateKeyException() {
...@@ -502,10 +501,6 @@ public class PageDataIndex extends PageIndex { ...@@ -502,10 +501,6 @@ public class PageDataIndex extends PageIndex {
return mainIndexColumn; return mainIndexColumn;
} }
int getMemorySizePerPage() {
return memorySizePerPage;
}
public String toString() { public String toString() {
return getName(); return getName();
} }
...@@ -528,4 +523,22 @@ public class PageDataIndex extends PageIndex { ...@@ -528,4 +523,22 @@ public class PageDataIndex extends PageIndex {
return table.getSQL() + ".tableScan"; return table.getSQL() + ".tableScan";
} }
int getMemoryPerPage() {
return memoryPerPage;
}
/**
* The memory usage of a page was changed. The new value is used to adopt
* the average estimated memory size of a page.
*
* @param x the new memory size
*/
void memoryChange(int x) {
if (memoryCount < Constants.MEMORY_FACTOR) {
memoryPerPage += (x - memoryPerPage) / ++memoryCount;
} else {
memoryPerPage += (x > memoryPerPage ? 1 : -1) + ((x - memoryPerPage) / Constants.MEMORY_FACTOR);
}
}
} }
...@@ -10,6 +10,7 @@ import java.lang.ref.SoftReference; ...@@ -10,6 +10,7 @@ import java.lang.ref.SoftReference;
import java.util.Arrays; import java.util.Arrays;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row; import org.h2.result.Row;
...@@ -65,7 +66,7 @@ public class PageDataLeaf extends PageData { ...@@ -65,7 +66,7 @@ public class PageDataLeaf extends PageData {
private int columnCount; private int columnCount;
private int memorySize; private int memoryData;
private PageDataLeaf(PageDataIndex index, int pageId, Data data) { private PageDataLeaf(PageDataIndex index, int pageId, Data data) {
super(index, pageId, data); super(index, pageId, data);
...@@ -204,7 +205,6 @@ public class PageDataLeaf extends PageData { ...@@ -204,7 +205,6 @@ public class PageDataLeaf extends PageData {
newOffsets[x] = offset; newOffsets[x] = offset;
newKeys[x] = row.getKey(); newKeys[x] = row.getKey();
newRows[x] = row; newRows[x] = row;
memorySize += row.getMemorySize();
offsets = newOffsets; offsets = newOffsets;
keys = newKeys; keys = newKeys;
rows = newRows; rows = newRows;
...@@ -226,7 +226,8 @@ public class PageDataLeaf extends PageData { ...@@ -226,7 +226,8 @@ public class PageDataLeaf extends PageData {
this.overflowRowSize = pageSize + rowLength; this.overflowRowSize = pageSize + rowLength;
writeData(); writeData();
// free up the space used by the row // free up the space used by the row
rowRef = new SoftReference<Row>(rows[0]); Row r = rows[0];
rowRef = new SoftReference<Row>(r);
rows[0] = null; rows[0] = null;
Data all = index.getPageStore().createData(); Data all = index.getPageStore().createData();
all.checkCapacity(data.length()); all.checkCapacity(data.length());
...@@ -251,6 +252,11 @@ public class PageDataLeaf extends PageData { ...@@ -251,6 +252,11 @@ public class PageDataLeaf extends PageData {
page = next; page = next;
} while (remaining > 0); } while (remaining > 0);
} }
if (rowRef == null) {
memoryChange(true, row);
} else {
memoryChange(true, null);
}
return -1; return -1;
} }
...@@ -261,7 +267,7 @@ public class PageDataLeaf extends PageData { ...@@ -261,7 +267,7 @@ public class PageDataLeaf extends PageData {
readAllRows(); readAllRows();
Row r = rows[i]; Row r = rows[i];
if (r != null) { if (r != null) {
memorySize += r.getMemorySize(); memoryChange(false, r);
} }
entryCount--; entryCount--;
if (entryCount < 0) { if (entryCount < 0) {
...@@ -336,7 +342,7 @@ public class PageDataLeaf extends PageData { ...@@ -336,7 +342,7 @@ public class PageDataLeaf extends PageData {
rowRef = new SoftReference<Row>(r); rowRef = new SoftReference<Row>(r);
} else { } else {
rows[at] = r; rows[at] = r;
memorySize += r.getMemorySize(); memoryChange(true, r);
} }
} }
return r; return r;
...@@ -551,8 +557,10 @@ public class PageDataLeaf extends PageData { ...@@ -551,8 +557,10 @@ public class PageDataLeaf extends PageData {
index.getPageStore().update(this); index.getPageStore().update(this);
} }
public int getMemorySize() { private void memoryChange(boolean add, Row r) {
return index.getMemorySizePerPage(); int diff = r == null ? 0 : 4 + 8 + Constants.MEMORY_POINTER + r.getMemory();
memoryData += add ? diff : -diff;
index.memoryChange((Constants.MEMORY_PAGE_DATA + memoryData + index.getPageStore().getPageSize()) >> 2);
} }
} }
...@@ -8,6 +8,7 @@ package org.h2.index; ...@@ -8,6 +8,7 @@ package org.h2.index;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.Data; import org.h2.store.Data;
...@@ -198,9 +199,8 @@ public class PageDataOverflow extends Page { ...@@ -198,9 +199,8 @@ public class PageDataOverflow extends Page {
* *
* @return number of double words (4 bytes) * @return number of double words (4 bytes)
*/ */
public int getMemorySize() { public int getMemory() {
// double the byte array size return (Constants.MEMORY_PAGE_DATA_OVERFLOW + store.getPageSize()) >> 2;
return store.getPageSize() >> 1;
} }
void setParentPageId(int parent) { void setParentPageId(int parent) {
......
...@@ -142,7 +142,7 @@ public class ResultTempTable implements ResultExternal { ...@@ -142,7 +142,7 @@ public class ResultTempTable implements ResultExternal {
private Row convertToRow(Value[] values) { private Row convertToRow(Value[] values) {
ValueArray data = ValueArray.get(values); ValueArray data = ValueArray.get(values);
return new Row(new Value[]{data}, data.getMemory()); return new Row(new Value[]{data}, Row.MEMORY_CALCULATE);
} }
private Cursor find(Row row) { private Cursor find(Row row) {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
package org.h2.result; package org.h2.result;
import org.h2.engine.Constants;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -26,7 +27,7 @@ public class Row implements SearchRow { ...@@ -26,7 +27,7 @@ public class Row implements SearchRow {
public Row(Value[] data, int memory) { public Row(Value[] data, int memory) {
this.data = data; this.data = data;
if (memory != MEMORY_CALCULATE) { if (memory != MEMORY_CALCULATE) {
this.memory = 16 + memory * 4; this.memory = memory;
} else { } else {
this.memory = MEMORY_CALCULATE; this.memory = MEMORY_CALCULATE;
} }
...@@ -83,13 +84,20 @@ public class Row implements SearchRow { ...@@ -83,13 +84,20 @@ public class Row implements SearchRow {
return data.length; return data.length;
} }
public int getMemorySize() { public int getMemory() {
if (memory != MEMORY_CALCULATE) { if (memory != MEMORY_CALCULATE) {
return memory; return memory;
} }
int m = 8; int m = Constants.MEMORY_ROW;
for (int i = 0; data != null && i < data.length; i++) { if (data != null) {
m += data[i].getMemory(); int len = data.length;
m += Constants.MEMORY_OBJECT + len * Constants.MEMORY_POINTER;
for (int i = 0; i < len; i++) {
Value v = data[i];
if (v != null) {
m += v.getMemory();
}
}
} }
return m; return m;
} }
......
...@@ -48,7 +48,7 @@ public class RowList { ...@@ -48,7 +48,7 @@ public class RowList {
private void writeRow(Data buff, Row r) { private void writeRow(Data buff, Row r) {
buff.checkCapacity(1 + Data.LENGTH_INT * 8); buff.checkCapacity(1 + Data.LENGTH_INT * 8);
buff.writeByte((byte) 1); buff.writeByte((byte) 1);
buff.writeInt(r.getMemorySize()); buff.writeInt(r.getMemory());
buff.writeInt(r.getColumnCount()); buff.writeInt(r.getColumnCount());
buff.writeLong(r.getKey()); buff.writeLong(r.getKey());
buff.writeInt(r.getVersion()); buff.writeInt(r.getVersion());
...@@ -126,7 +126,7 @@ public class RowList { ...@@ -126,7 +126,7 @@ public class RowList {
*/ */
public void add(Row r) { public void add(Row r) {
list.add(r); list.add(r);
memory += r.getMemorySize(); memory += r.getMemory() + Constants.MEMORY_POINTER;
if (maxMemory > 0 && memory > maxMemory) { if (maxMemory > 0 && memory > maxMemory) {
writeAllRows(); writeAllRows();
} }
......
...@@ -65,4 +65,11 @@ public interface SearchRow { ...@@ -65,4 +65,11 @@ public interface SearchRow {
*/ */
long getKey(); long getKey();
/**
* Get the estimated memory used for this row, in bytes.
*
* @return the memory
*/
int getMemory();
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
package org.h2.result; package org.h2.result;
import org.h2.engine.Constants;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -17,6 +18,7 @@ public class SimpleRow implements SearchRow { ...@@ -17,6 +18,7 @@ public class SimpleRow implements SearchRow {
private long key; private long key;
private int version; private int version;
private Value[] data; private Value[] data;
private int memory;
public SimpleRow(Value[] data) { public SimpleRow(Value[] data) {
this.data = data; this.data = data;
...@@ -65,4 +67,18 @@ public class SimpleRow implements SearchRow { ...@@ -65,4 +67,18 @@ public class SimpleRow implements SearchRow {
return buff.append(')').toString(); return buff.append(')').toString();
} }
public int getMemory() {
if (memory == 0) {
int len = data.length;
memory = Constants.MEMORY_OBJECT + len * Constants.MEMORY_POINTER;
for (int i = 0; i < len; i++) {
Value v = data[i];
if (v != null) {
memory += v.getMemory();
}
}
}
return memory;
}
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
package org.h2.result; package org.h2.result;
import org.h2.engine.Constants;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -57,4 +58,8 @@ public class SimpleRowValue implements SearchRow { ...@@ -57,4 +58,8 @@ public class SimpleRowValue implements SearchRow {
return "( /* " + key + " */ " + (data == null ? "null" : data.getTraceSQL()) + " )"; return "( /* " + key + " */ " + (data == null ? "null" : data.getTraceSQL()) + " )";
} }
public int getMemory() {
return Constants.MEMORY_OBJECT + (data == null ? 0 : data.getMemory());
}
} }
...@@ -194,7 +194,7 @@ public class PageFreeList extends Page { ...@@ -194,7 +194,7 @@ public class PageFreeList extends Page {
* *
* @return number of double words (4 bytes) * @return number of double words (4 bytes)
*/ */
public int getMemorySize() { public int getMemory() {
return store.getPageSize() >> 2; return store.getPageSize() >> 2;
} }
......
...@@ -141,7 +141,7 @@ public class PageStreamData extends Page { ...@@ -141,7 +141,7 @@ public class PageStreamData extends Page {
* *
* @return number of double words (4 bytes) * @return number of double words (4 bytes)
*/ */
public int getMemorySize() { public int getMemory() {
return store.getPageSize() >> 2; return store.getPageSize() >> 2;
} }
......
...@@ -178,7 +178,7 @@ public class PageStreamTrunk extends Page { ...@@ -178,7 +178,7 @@ public class PageStreamTrunk extends Page {
* *
* @return number of double words (4 bytes) * @return number of double words (4 bytes)
*/ */
public int getMemorySize() { public int getMemory() {
return store.getPageSize() >> 2; return store.getPageSize() >> 2;
} }
......
...@@ -891,8 +891,8 @@ public class MetaTable extends Table { ...@@ -891,8 +891,8 @@ public class MetaTable extends Table {
add(rows, "info.FILE_WRITE_TOTAL", "" + store.getWriteCountTotal()); add(rows, "info.FILE_WRITE_TOTAL", "" + store.getWriteCountTotal());
add(rows, "info.FILE_WRITE", "" + store.getWriteCount()); add(rows, "info.FILE_WRITE", "" + store.getWriteCount());
add(rows, "info.FILE_READ", "" + store.getReadCount()); add(rows, "info.FILE_READ", "" + store.getReadCount());
add(rows, "info.CACHE_MAX_SIZE", "" + store.getCache().getMaxSize()); add(rows, "info.CACHE_MAX_SIZE", "" + store.getCache().getMaxMemory());
add(rows, "info.CACHE_SIZE", "" + store.getCache().getSize()); add(rows, "info.CACHE_SIZE", "" + store.getCache().getMemory());
} }
break; break;
} }
......
...@@ -599,7 +599,7 @@ public class RegularTable extends TableBase { ...@@ -599,7 +599,7 @@ public class RegularTable extends TableBase {
* @return the row * @return the row
*/ */
public Row createRow(Value[] data) { public Row createRow(Value[] data) {
return new Row(data, memoryPerRow); return new Row(data, Row.MEMORY_CALCULATE);
} }
/** /**
......
...@@ -492,7 +492,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -492,7 +492,7 @@ public abstract class Table extends SchemaObjectBase {
} }
public Row getTemplateRow() { public Row getTemplateRow() {
return new Row(new Value[columns.length], memoryPerRow); return new Row(new Value[columns.length], Row.MEMORY_CALCULATE);
} }
/** /**
......
...@@ -73,20 +73,20 @@ public interface Cache { ...@@ -73,20 +73,20 @@ public interface Cache {
* *
* @param size the maximum size in KB * @param size the maximum size in KB
*/ */
void setMaxSize(int size); void setMaxMemory(int size);
/** /**
* Get the maximum size in KB. * Get the maximum memory to be used.
* *
* @return the maximum size in KB * @return the maximum size in KB
*/ */
int getMaxSize(); int getMaxMemory();
/** /**
* Get the used size in KB. * Get the used size in KB.
* *
* @return the current size in KB * @return the current size in KB
*/ */
int getSize(); int getMemory();
} }
...@@ -15,7 +15,7 @@ public class CacheHead extends CacheObject { ...@@ -15,7 +15,7 @@ public class CacheHead extends CacheObject {
return false; return false;
} }
public int getMemorySize() { public int getMemory() {
return 0; return 0;
} }
......
...@@ -34,17 +34,17 @@ public class CacheLRU implements Cache { ...@@ -34,17 +34,17 @@ public class CacheLRU implements Cache {
/** /**
* The maximum memory, in words (4 bytes each). * The maximum memory, in words (4 bytes each).
*/ */
private int maxSize; private int maxMemory;
/** /**
* The current memory used in this cache, in words (4 bytes each). * The current memory used in this cache, in words (4 bytes each).
*/ */
private int sizeMemory; private int memory;
private CacheLRU(CacheWriter writer, int maxKb) { private CacheLRU(CacheWriter writer, int maxMemoryKb) {
this.maxSize = maxKb * 1024 / 4; this.setMaxMemory(maxMemoryKb);
this.writer = writer; this.writer = writer;
this.len = MathUtils.nextPowerOf2(maxSize / 64); this.len = MathUtils.nextPowerOf2(maxMemory / 64);
this.mask = len - 1; this.mask = len - 1;
MathUtils.checkPowerOf2(len); MathUtils.checkPowerOf2(len);
clear(); clear();
...@@ -82,7 +82,7 @@ public class CacheLRU implements Cache { ...@@ -82,7 +82,7 @@ public class CacheLRU implements Cache {
values = null; values = null;
values = new CacheObject[len]; values = new CacheObject[len];
recordCount = 0; recordCount = 0;
sizeMemory = 0; memory = len * Constants.MEMORY_POINTER;
} }
public void put(CacheObject rec) { public void put(CacheObject rec) {
...@@ -97,7 +97,7 @@ public class CacheLRU implements Cache { ...@@ -97,7 +97,7 @@ public class CacheLRU implements Cache {
rec.cacheChained = values[index]; rec.cacheChained = values[index];
values[index] = rec; values[index] = rec;
recordCount++; recordCount++;
sizeMemory += rec.getMemorySize(); memory += rec.getMemory();
addToFront(rec); addToFront(rec);
removeOldIfRequired(); removeOldIfRequired();
} }
...@@ -120,7 +120,7 @@ public class CacheLRU implements Cache { ...@@ -120,7 +120,7 @@ public class CacheLRU implements Cache {
private void removeOldIfRequired() { private void removeOldIfRequired() {
// a small method, to allow inlining // a small method, to allow inlining
if (sizeMemory >= maxSize) { if (memory >= maxMemory) {
removeOld(); removeOld();
} }
} }
...@@ -128,11 +128,11 @@ public class CacheLRU implements Cache { ...@@ -128,11 +128,11 @@ public class CacheLRU implements Cache {
private void removeOld() { private void removeOld() {
int i = 0; int i = 0;
ArrayList<CacheObject> changed = New.arrayList(); ArrayList<CacheObject> changed = New.arrayList();
int mem = sizeMemory; int mem = memory;
int rc = recordCount; int rc = recordCount;
boolean flushed = false; boolean flushed = false;
CacheObject next = head.cacheNext; CacheObject next = head.cacheNext;
while (mem * 4 > maxSize * 3 && rc > Constants.CACHE_MIN_RECORDS) { while (mem * 4 > maxMemory * 3 && rc > Constants.CACHE_MIN_RECORDS) {
CacheObject check = next; CacheObject check = next;
next = check.cacheNext; next = check.cacheNext;
i++; i++;
...@@ -144,7 +144,7 @@ public class CacheLRU implements Cache { ...@@ -144,7 +144,7 @@ public class CacheLRU implements Cache {
} else { } else {
// can't remove any record, because the records can not be removed // can't remove any record, because the records can not be removed
// hopefully this does not happen frequently, but it can happen // hopefully this does not happen frequently, but it can happen
writer.getTrace().info("Cannot remove records, cache size too small? records:" + recordCount + " memory:" + sizeMemory); writer.getTrace().info("Cannot remove records, cache size too small? records:" + recordCount + " memory:" + memory);
break; break;
} }
} }
...@@ -160,7 +160,7 @@ public class CacheLRU implements Cache { ...@@ -160,7 +160,7 @@ public class CacheLRU implements Cache {
continue; continue;
} }
rc--; rc--;
mem -= check.getMemorySize(); mem -= check.getMemory();
if (check.isChanged()) { if (check.isChanged()) {
changed.add(check); changed.add(check);
} else { } else {
...@@ -169,17 +169,17 @@ public class CacheLRU implements Cache { ...@@ -169,17 +169,17 @@ public class CacheLRU implements Cache {
} }
if (changed.size() > 0) { if (changed.size() > 0) {
Collections.sort(changed); Collections.sort(changed);
int max = maxSize; int max = maxMemory;
try { try {
// temporary disable size checking, // temporary disable size checking,
// to avoid stack overflow // to avoid stack overflow
maxSize = Integer.MAX_VALUE; maxMemory = Integer.MAX_VALUE;
for (i = 0; i < changed.size(); i++) { for (i = 0; i < changed.size(); i++) {
CacheObject rec = changed.get(i); CacheObject rec = changed.get(i);
writer.writeBack(rec); writer.writeBack(rec);
} }
} finally { } finally {
maxSize = max; maxMemory = max;
} }
for (i = 0; i < changed.size(); i++) { for (i = 0; i < changed.size(); i++) {
CacheObject rec = changed.get(i); CacheObject rec = changed.get(i);
...@@ -235,7 +235,7 @@ public class CacheLRU implements Cache { ...@@ -235,7 +235,7 @@ public class CacheLRU implements Cache {
last.cacheChained = rec.cacheChained; last.cacheChained = rec.cacheChained;
} }
recordCount--; recordCount--;
sizeMemory -= rec.getMemorySize(); memory -= rec.getMemory();
removeFromLinkedList(rec); removeFromLinkedList(rec);
if (SysProperties.CHECK) { if (SysProperties.CHECK) {
rec.cacheChained = null; rec.cacheChained = null;
...@@ -307,20 +307,26 @@ public class CacheLRU implements Cache { ...@@ -307,20 +307,26 @@ public class CacheLRU implements Cache {
return list; return list;
} }
public void setMaxSize(int maxKb) { public void setMaxMemory(int maxKb) {
int newSize = maxKb * 1024 / 4; int newSize = MathUtils.convertLongToInt(maxKb * 1024L / 4);
maxSize = newSize < 0 ? 0 : newSize; maxMemory = newSize < 0 ? 0 : newSize;
// can not resize, otherwise existing records are lost // can not resize, otherwise existing records are lost
// resize(maxSize); // resize(maxSize);
removeOldIfRequired(); removeOldIfRequired();
} }
public int getMaxSize() { public int getMaxMemory() {
return maxSize * 4 / 1024; return (int) (maxMemory * 4L / 1024);
} }
public int getSize() { public int getMemory() {
return sizeMemory * 4 / 1024; // CacheObject rec = head.cacheNext;
// while (rec != head) {
// System.out.println(rec.getMemory() + " " +
// MemoryFootprint.getObjectSize(rec) + " " + rec);
// rec = rec.cacheNext;
// }
return (int) (memory * 4L / 1024);
} }
} }
......
...@@ -43,11 +43,11 @@ public abstract class CacheObject implements Comparable<CacheObject> { ...@@ -43,11 +43,11 @@ public abstract class CacheObject implements Comparable<CacheObject> {
public abstract boolean canRemove(); public abstract boolean canRemove();
/** /**
* Get the estimated memory size. * Get the estimated used memory.
* *
* @return number of double words (4 bytes) * @return number of words (one word is 4 bytes)
*/ */
public abstract int getMemorySize(); public abstract int getMemory();
public void setPos(int pos) { public void setPos(int pos) {
if (SysProperties.CHECK && (cachePrevious != null || cacheNext != null || cacheChained != null)) { if (SysProperties.CHECK && (cachePrevious != null || cacheNext != null || cacheChained != null)) {
......
...@@ -48,12 +48,12 @@ class CacheSecondLevel implements Cache { ...@@ -48,12 +48,12 @@ class CacheSecondLevel implements Cache {
return baseCache.getAllChanged(); return baseCache.getAllChanged();
} }
public int getMaxSize() { public int getMaxMemory() {
return baseCache.getMaxSize(); return baseCache.getMaxMemory();
} }
public int getSize() { public int getMemory() {
return baseCache.getSize(); return baseCache.getMemory();
} }
public void put(CacheObject r) { public void put(CacheObject r) {
...@@ -66,8 +66,8 @@ class CacheSecondLevel implements Cache { ...@@ -66,8 +66,8 @@ class CacheSecondLevel implements Cache {
map.remove(pos); map.remove(pos);
} }
public void setMaxSize(int size) { public void setMaxMemory(int size) {
baseCache.setMaxSize(size); baseCache.setMaxMemory(size);
} }
public CacheObject update(int pos, CacheObject record) { public CacheObject update(int pos, CacheObject record) {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
package org.h2.util; package org.h2.util;
import java.lang.instrument.Instrumentation;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
...@@ -15,6 +16,7 @@ import org.h2.engine.Constants; ...@@ -15,6 +16,7 @@ import org.h2.engine.Constants;
* A simple CPU profiling tool similar to java -Xrunhprof. * A simple CPU profiling tool similar to java -Xrunhprof.
*/ */
public class Profiler implements Runnable { public class Profiler implements Runnable {
private static Instrumentation instrumentation;
private static final int MAX_ELEMENTS = 1000; private static final int MAX_ELEMENTS = 1000;
public int interval = 10; public int interval = 10;
...@@ -29,7 +31,8 @@ public class Profiler implements Runnable { ...@@ -29,7 +31,8 @@ public class Profiler implements Runnable {
"java.lang.UNIXProcess.waitForProcessExit," + "java.lang.UNIXProcess.waitForProcessExit," +
"java.lang.Object.wait," + "java.lang.Object.wait," +
"java.lang.Thread.sleep," + "java.lang.Thread.sleep," +
"sun.awt.windows.WToolkit.eventLoop," "sun.awt.windows.WToolkit.eventLoop," +
"sun.misc.Unsafe.park,"
, ',', true); , ',', true);
private volatile boolean stop; private volatile boolean stop;
private HashMap<String, Integer> counts = new HashMap<String, Integer>(); private HashMap<String, Integer> counts = new HashMap<String, Integer>();
...@@ -38,6 +41,25 @@ public class Profiler implements Runnable { ...@@ -38,6 +41,25 @@ public class Profiler implements Runnable {
private Thread thread; private Thread thread;
private long time; private long time;
/**
* This method is called when the agent is installed.
*
* @param agentArgs the agent arguments
* @param inst the instrumentation object
*/
public static void premain(String agentArgs, Instrumentation inst) {
instrumentation = inst;
}
/**
* Get the instrumentation object if started as an agent.
*
* @return the instrumentation, or null
*/
public static Instrumentation getInstrumentation() {
return instrumentation;
}
/** /**
* Start collecting profiling data. * Start collecting profiling data.
*/ */
......
...@@ -437,29 +437,23 @@ public class Utils { ...@@ -437,29 +437,23 @@ public class Utils {
* @return the resource data * @return the resource data
*/ */
public static byte[] getResource(String name) throws IOException { public static byte[] getResource(String name) throws IOException {
byte[] data; byte[] data = RESOURCES.get(name);
if (RESOURCES.size() == 0) { if (data == null) {
// TODO web: security (check what happens with files like 'lpt1.txt' on windows) data = loadResource(name);
InputStream in = Utils.class.getResourceAsStream(name); RESOURCES.put(name, data);
if (in == null) {
data = null;
} else {
data = IOUtils.readBytesAndClose(in, 0);
}
} else {
data = RESOURCES.get(name);
} }
return data == null ? EMPTY_BYTES : data; return data == null ? EMPTY_BYTES : data;
} }
static { private static byte[] loadResource(String name) throws IOException {
loadResourcesFromZip();
}
private static void loadResourcesFromZip() {
InputStream in = Utils.class.getResourceAsStream("data.zip"); InputStream in = Utils.class.getResourceAsStream("data.zip");
if (in == null) { if (in == null) {
return; in = Utils.class.getResourceAsStream(name);
if (in == null) {
return null;
} else {
return IOUtils.readBytesAndClose(in, 0);
}
} }
ZipInputStream zipIn = new ZipInputStream(in); ZipInputStream zipIn = new ZipInputStream(in);
try { try {
...@@ -472,16 +466,22 @@ public class Utils { ...@@ -472,16 +466,22 @@ public class Utils {
if (!entryName.startsWith("/")) { if (!entryName.startsWith("/")) {
entryName = "/" + entryName; entryName = "/" + entryName;
} }
ByteArrayOutputStream out = new ByteArrayOutputStream(); if (entryName.equals(name)) {
IOUtils.copy(zipIn, out); ByteArrayOutputStream out = new ByteArrayOutputStream();
zipIn.closeEntry(); IOUtils.copy(zipIn, out);
RESOURCES.put(entryName, out.toByteArray()); zipIn.closeEntry();
return out.toByteArray();
} else {
zipIn.closeEntry();
}
} }
zipIn.close();
} catch (IOException e) { } catch (IOException e) {
// if this happens we have a real problem // if this happens we have a real problem
e.printStackTrace(); e.printStackTrace();
} finally {
zipIn.close();
} }
return null;
} }
/** /**
......
...@@ -198,132 +198,144 @@ public class DataType { ...@@ -198,132 +198,144 @@ public class DataType {
add(Value.NULL, Types.NULL, "Null", add(Value.NULL, Types.NULL, "Null",
new DataType(), new DataType(),
new String[]{"NULL"}, new String[]{"NULL"},
1 // the value is always in the cache
0
); );
add(Value.STRING, Types.VARCHAR, "String", add(Value.STRING, Types.VARCHAR, "String",
createString(true), createString(true),
new String[]{"VARCHAR", "VARCHAR2", "NVARCHAR", "NVARCHAR2", "VARCHAR_CASESENSITIVE", "CHARACTER VARYING", "TID"}, new String[]{"VARCHAR", "VARCHAR2", "NVARCHAR", "NVARCHAR2", "VARCHAR_CASESENSITIVE", "CHARACTER VARYING", "TID"},
4 // 24 for ValueString, 24 for String
48
); );
add(Value.STRING, Types.LONGVARCHAR, "String", add(Value.STRING, Types.LONGVARCHAR, "String",
createString(true), createString(true),
new String[]{"LONGVARCHAR", "LONGNVARCHAR"}, new String[]{"LONGVARCHAR", "LONGNVARCHAR"},
4 48
); );
add(Value.STRING_FIXED, Types.CHAR, "String", add(Value.STRING_FIXED, Types.CHAR, "String",
createString(true), createString(true),
new String[]{"CHAR", "CHARACTER", "NCHAR"}, new String[]{"CHAR", "CHARACTER", "NCHAR"},
4 48
); );
add(Value.STRING_IGNORECASE, Types.VARCHAR, "String", add(Value.STRING_IGNORECASE, Types.VARCHAR, "String",
createString(false), createString(false),
new String[]{"VARCHAR_IGNORECASE"}, new String[]{"VARCHAR_IGNORECASE"},
4 48
); );
add(Value.BOOLEAN, DataType.TYPE_BOOLEAN, "Boolean", add(Value.BOOLEAN, DataType.TYPE_BOOLEAN, "Boolean",
createDecimal(ValueBoolean.PRECISION, ValueBoolean.PRECISION, 0, ValueBoolean.DISPLAY_SIZE, false, false), createDecimal(ValueBoolean.PRECISION, ValueBoolean.PRECISION, 0, ValueBoolean.DISPLAY_SIZE, false, false),
new String[]{"BOOLEAN", "BIT", "BOOL"}, new String[]{"BOOLEAN", "BIT", "BOOL"},
1 // the value is always in the cache
0
); );
add(Value.BYTE, Types.TINYINT, "Byte", add(Value.BYTE, Types.TINYINT, "Byte",
createDecimal(ValueByte.PRECISION, ValueByte.PRECISION, 0, ValueByte.DISPLAY_SIZE, false, false), createDecimal(ValueByte.PRECISION, ValueByte.PRECISION, 0, ValueByte.DISPLAY_SIZE, false, false),
new String[]{"TINYINT"}, new String[]{"TINYINT"},
// the value is almost always in the cache
1 1
); );
add(Value.SHORT, Types.SMALLINT, "Short", add(Value.SHORT, Types.SMALLINT, "Short",
createDecimal(ValueShort.PRECISION, ValueShort.PRECISION, 0, ValueShort.DISPLAY_SIZE, false, false), createDecimal(ValueShort.PRECISION, ValueShort.PRECISION, 0, ValueShort.DISPLAY_SIZE, false, false),
new String[]{"SMALLINT", "YEAR", "INT2"}, new String[]{"SMALLINT", "YEAR", "INT2"},
5 // in many cases the value is in the cache
20
); );
add(Value.INT, Types.INTEGER, "Int", add(Value.INT, Types.INTEGER, "Int",
createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, ValueInt.DISPLAY_SIZE, false, false), createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, ValueInt.DISPLAY_SIZE, false, false),
new String[]{"INTEGER", "INT", "MEDIUMINT", "INT4", "SIGNED"}, new String[]{"INTEGER", "INT", "MEDIUMINT", "INT4", "SIGNED"},
5 // in many cases the value is in the cache
20
); );
add(Value.LONG, Types.BIGINT, "Long", add(Value.LONG, Types.BIGINT, "Long",
createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, false), createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, false),
new String[]{"BIGINT", "INT8", "LONG"}, new String[]{"BIGINT", "INT8", "LONG"},
5 24
); );
add(Value.LONG, Types.BIGINT, "Long", add(Value.LONG, Types.BIGINT, "Long",
createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, true), createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, true),
new String[]{"IDENTITY", "SERIAL"}, new String[]{"IDENTITY", "SERIAL"},
5 24
); );
add(Value.DECIMAL, Types.DECIMAL, "BigDecimal", add(Value.DECIMAL, Types.DECIMAL, "BigDecimal",
createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false), createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false),
new String[]{"DECIMAL", "DEC"}, new String[]{"DECIMAL", "DEC"},
17 // 40 for ValueDecimal,
64
); );
add(Value.DECIMAL, Types.NUMERIC, "BigDecimal", add(Value.DECIMAL, Types.NUMERIC, "BigDecimal",
createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false), createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false),
new String[]{"NUMERIC", "NUMBER"}, new String[]{"NUMERIC", "NUMBER"},
17 64
); );
add(Value.FLOAT, Types.REAL, "Float", add(Value.FLOAT, Types.REAL, "Float",
createDecimal(ValueFloat.PRECISION, ValueFloat.PRECISION, 0, ValueFloat.DISPLAY_SIZE, false, false), createDecimal(ValueFloat.PRECISION, ValueFloat.PRECISION, 0, ValueFloat.DISPLAY_SIZE, false, false),
new String[] {"REAL", "FLOAT4"}, new String[] {"REAL", "FLOAT4"},
5 24
); );
add(Value.DOUBLE, Types.DOUBLE, "Double", add(Value.DOUBLE, Types.DOUBLE, "Double",
createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false), createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false),
new String[] { "DOUBLE", "DOUBLE PRECISION" }, new String[] { "DOUBLE", "DOUBLE PRECISION" },
4 24
); );
add(Value.DOUBLE, Types.FLOAT, "Double", add(Value.DOUBLE, Types.FLOAT, "Double",
createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false), createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false),
new String[] {"FLOAT", "FLOAT8" }, new String[] {"FLOAT", "FLOAT8" },
4 24
); );
add(Value.TIME, Types.TIME, "Time", add(Value.TIME, Types.TIME, "Time",
createDate(ValueTime.PRECISION, "TIME", 0, ValueTime.DISPLAY_SIZE), createDate(ValueTime.PRECISION, "TIME", 0, ValueTime.DISPLAY_SIZE),
new String[]{"TIME"}, new String[]{"TIME"},
10 // 24 for ValueTime, 32 for java.sql.Time
56
); );
add(Value.DATE, Types.DATE, "Date", add(Value.DATE, Types.DATE, "Date",
createDate(ValueDate.PRECISION, "DATE", 0, ValueDate.DISPLAY_SIZE), createDate(ValueDate.PRECISION, "DATE", 0, ValueDate.DISPLAY_SIZE),
new String[]{"DATE"}, new String[]{"DATE"},
10 // 24 for ValueDate, 32 for java.sql.Data
56
); );
add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp", add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp",
createDate(ValueTimestamp.PRECISION, "TIMESTAMP", ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE), createDate(ValueTimestamp.PRECISION, "TIMESTAMP", ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE),
new String[]{"TIMESTAMP", "DATETIME", "SMALLDATETIME"}, new String[]{"TIMESTAMP", "DATETIME", "SMALLDATETIME"},
12 // 24 for ValueTimestamp, 32 for java.sql.Timestamp
56
); );
add(Value.BYTES, Types.VARBINARY, "Bytes", add(Value.BYTES, Types.VARBINARY, "Bytes",
createString(false), createString(false),
new String[]{"VARBINARY"}, new String[]{"VARBINARY"},
8 32
); );
add(Value.BYTES, Types.BINARY, "Bytes", add(Value.BYTES, Types.BINARY, "Bytes",
createString(false), createString(false),
new String[]{"BINARY", "RAW", "BYTEA", "LONG RAW"}, new String[]{"BINARY", "RAW", "BYTEA", "LONG RAW"},
8 32
); );
add(Value.BYTES, Types.LONGVARBINARY, "Bytes", add(Value.BYTES, Types.LONGVARBINARY, "Bytes",
createString(false), createString(false),
new String[]{"LONGVARBINARY"}, new String[]{"LONGVARBINARY"},
8 32
); );
add(Value.UUID, Types.BINARY, "Bytes", add(Value.UUID, Types.BINARY, "Bytes",
createString(false), createString(false),
new String[]{"UUID"}, new String[]{"UUID"},
8 32
); );
add(Value.JAVA_OBJECT, Types.OTHER, "Object", add(Value.JAVA_OBJECT, Types.OTHER, "Object",
createString(false), createString(false),
new String[]{"OTHER", "OBJECT", "JAVA_OBJECT"}, new String[]{"OTHER", "OBJECT", "JAVA_OBJECT"},
8 24
); );
add(Value.BLOB, Types.BLOB, "Blob", add(Value.BLOB, Types.BLOB, "Blob",
createLob(), createLob(),
new String[]{"BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "IMAGE", "OID"}, new String[]{"BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "IMAGE", "OID"},
10 // 80 for ValueLob, 24 for String
104
); );
add(Value.CLOB, Types.CLOB, "Clob", add(Value.CLOB, Types.CLOB, "Clob",
createLob(), createLob(),
new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "NTEXT", "NCLOB"}, new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "NTEXT", "NCLOB"},
10 // 80 for ValueLob, 24 for String
104
); );
DataType dataType = new DataType(); DataType dataType = new DataType();
dataType.prefix = "("; dataType.prefix = "(";
...@@ -331,13 +343,13 @@ public class DataType { ...@@ -331,13 +343,13 @@ public class DataType {
add(Value.ARRAY, Types.ARRAY, "Array", add(Value.ARRAY, Types.ARRAY, "Array",
dataType, dataType,
new String[]{"ARRAY"}, new String[]{"ARRAY"},
10 32
); );
dataType = new DataType(); dataType = new DataType();
add(Value.RESULT_SET, 0, "ResultSet", add(Value.RESULT_SET, 0, "ResultSet",
dataType, dataType,
new String[]{"RESULT_SET"}, new String[]{"RESULT_SET"},
20 400
); );
for (int i = 0; i < TYPES_BY_VALUE_TYPE.size(); i++) { for (int i = 0; i < TYPES_BY_VALUE_TYPE.size(); i++) {
DataType dt = TYPES_BY_VALUE_TYPE.get(i); DataType dt = TYPES_BY_VALUE_TYPE.get(i);
......
...@@ -189,7 +189,7 @@ public abstract class Value { ...@@ -189,7 +189,7 @@ public abstract class Value {
* @return the memory used in bytes * @return the memory used in bytes
*/ */
public int getMemory() { public int getMemory() {
return DataType.getDataType(getType()).memory * 4; return DataType.getDataType(getType()).memory;
} }
/** /**
...@@ -352,6 +352,13 @@ public abstract class Value { ...@@ -352,6 +352,13 @@ public abstract class Value {
return v; return v;
} }
/**
* Clear the value cache. Used for testing.
*/
public static void clearCache() {
softCache = null;
}
public Boolean getBoolean() { public Boolean getBoolean() {
return ((ValueBoolean) convertTo(Value.BOOLEAN)).getBoolean(); return ((ValueBoolean) convertTo(Value.BOOLEAN)).getBoolean();
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package org.h2.value; package org.h2.value;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import org.h2.engine.Constants;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
...@@ -142,9 +143,9 @@ public class ValueArray extends Value { ...@@ -142,9 +143,9 @@ public class ValueArray extends Value {
} }
public int getMemory() { public int getMemory() {
int memory = 0; int memory = 32;
for (Value v : values) { for (Value v : values) {
memory += v.getMemory(); memory += v.getMemory() + Constants.MEMORY_POINTER;
} }
return memory; return memory;
} }
......
...@@ -110,7 +110,7 @@ public class ValueBytes extends Value { ...@@ -110,7 +110,7 @@ public class ValueBytes extends Value {
} }
public int getMemory() { public int getMemory() {
return value.length + 4; return value.length + 24;
} }
public boolean equals(Object other) { public boolean equals(Object other) {
......
...@@ -211,7 +211,7 @@ public class ValueDecimal extends Value { ...@@ -211,7 +211,7 @@ public class ValueDecimal extends Value {
} }
public int getMemory() { public int getMemory() {
return MathUtils.precision(value) * 3 + 120; return MathUtils.precision(value) + 120;
} }
} }
...@@ -760,9 +760,9 @@ public class ValueLob extends Value { ...@@ -760,9 +760,9 @@ public class ValueLob extends Value {
public int getMemory() { public int getMemory() {
if (small != null) { if (small != null) {
return small.length + 64; return small.length + 104;
} }
return 128; return 140;
} }
/** /**
......
...@@ -334,9 +334,9 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo ...@@ -334,9 +334,9 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
public int getMemory() { public int getMemory() {
if (small != null) { if (small != null) {
return small.length + 64; return small.length + 104;
} }
return 128; return 140;
} }
/** /**
......
...@@ -64,7 +64,7 @@ public class ValueString extends Value { ...@@ -64,7 +64,7 @@ public class ValueString extends Value {
} }
public int getMemory() { public int getMemory() {
return value.length() * 2 + 30; return value.length() * 2 + 48;
} }
public Value convertPrecision(long precision) { public Value convertPrecision(long precision) {
......
...@@ -8,6 +8,7 @@ package org.h2.test.unit; ...@@ -8,6 +8,7 @@ package org.h2.test.unit;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Random; import java.util.Random;
...@@ -18,6 +19,9 @@ import org.h2.util.Cache; ...@@ -18,6 +19,9 @@ import org.h2.util.Cache;
import org.h2.util.CacheLRU; import org.h2.util.CacheLRU;
import org.h2.util.CacheObject; import org.h2.util.CacheObject;
import org.h2.util.CacheWriter; import org.h2.util.CacheWriter;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.Value;
/** /**
* Tests the cache. * Tests the cache.
...@@ -32,24 +36,80 @@ public class TestCache extends TestBase implements CacheWriter { ...@@ -32,24 +36,80 @@ public class TestCache extends TestBase implements CacheWriter {
* @param a ignored * @param a ignored
*/ */
public static void main(String... a) throws Exception { public static void main(String... a) throws Exception {
TestBase.createCaller().init().test(); TestBase test = TestBase.createCaller().init();
// test.config.traceTest = true;
test.test();
} }
public void test() throws SQLException { public void test() throws SQLException {
// testCache(false); testMemoryUsage();
testCache(true); testCache();
testCacheDb(false); testCacheDb(false);
testCacheDb(true); testCacheDb(true);
} }
private void testCache(boolean lru) { private void testMemoryUsage() throws SQLException {
if (!config.traceTest) {
return;
}
if (config.memory) {
return;
}
deleteDb("cache");
Connection conn;
Statement stat;
ResultSet rs;
conn = getConnection("cache;CACHE_SIZE=16384");
stat = conn.createStatement();
// test DataOverflow
stat.execute("create table test(id int primary key, data varchar)");
stat.execute("set max_memory_undo 10000");
conn.close();
stat = null;
conn = null;
long before = getRealMemory();
conn = getConnection("cache;CACHE_SIZE=16384;DB_CLOSE_ON_EXIT=FALSE");
stat = conn.createStatement();
// -XX:+HeapDumpOnOutOfMemoryError
stat.execute("insert into test select x, random_uuid() || space(1) from system_range(1, 10000)");
// stat.execute("create index idx_test_n on test(data)");
// stat.execute("select data from test where data >= ''");
rs = stat.executeQuery("select value from information_schema.settings where name = 'info.CACHE_SIZE'");
rs.next();
int calculated = rs.getInt(1);
rs = null;
long afterInsert = getRealMemory();
conn.close();
stat = null;
conn = null;
rs = null;
long afterClose = getRealMemory();
trace("Used memory: " + (afterInsert - afterClose) + " calculated cache size: " + calculated);
trace("Before: " + before + " after: " + afterInsert + " after closing: " + afterClose);
}
private int getRealMemory() {
StringUtils.clearCache();
Value.clearCache();
eatMemory(100);
freeMemory();
System.gc();
return Utils.getMemoryUsed();
}
private void testCache() {
out = ""; out = "";
Cache c = CacheLRU.getCache(this, lru ? "LRU" : "TQ", 16); Cache c = CacheLRU.getCache(this, "LRU", 16);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
c.put(new Obj(i)); c.put(new Obj(i));
} }
assertEquals(lru ? "0 1 2 3 " : "4 5 6 ", out); assertEquals("0 1 2 3 ", out);
} }
/** /**
...@@ -61,7 +121,7 @@ public class TestCache extends TestBase implements CacheWriter { ...@@ -61,7 +121,7 @@ public class TestCache extends TestBase implements CacheWriter {
setPos(pos); setPos(pos);
} }
public int getMemorySize() { public int getMemory() {
return 1024; return 1024;
} }
......
...@@ -14,10 +14,13 @@ import java.sql.SQLException; ...@@ -14,10 +14,13 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Random; import java.util.Random;
import org.h2.engine.Constants;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
import org.h2.store.FileStore; import org.h2.store.FileStore;
import org.h2.store.LobStorage; import org.h2.store.LobStorage;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.utils.MemoryFootprint;
import org.h2.tools.SimpleResultSet;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
import org.h2.util.TempFileDeleter; import org.h2.util.TempFileDeleter;
import org.h2.util.Utils; import org.h2.util.Utils;
...@@ -34,6 +37,7 @@ import org.h2.value.ValueInt; ...@@ -34,6 +37,7 @@ import org.h2.value.ValueInt;
import org.h2.value.ValueJavaObject; import org.h2.value.ValueJavaObject;
import org.h2.value.ValueLong; import org.h2.value.ValueLong;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
import org.h2.value.ValueResultSet;
import org.h2.value.ValueShort; import org.h2.value.ValueShort;
import org.h2.value.ValueString; import org.h2.value.ValueString;
import org.h2.value.ValueStringFixed; import org.h2.value.ValueStringFixed;
...@@ -58,12 +62,21 @@ public class TestValueMemory extends TestBase implements DataHandler { ...@@ -58,12 +62,21 @@ public class TestValueMemory extends TestBase implements DataHandler {
* @param a ignored * @param a ignored
*/ */
public static void main(String... a) throws Exception { public static void main(String... a) throws Exception {
TestBase.createCaller().init().test(); // run using -javaagent:ext/h2-1.2.139.jar
TestBase test = TestBase.createCaller().init();
test.config.traceTest = true;
test.test();
} }
public void test() throws SQLException { public void test() throws SQLException {
testCompare(); testCompare();
for (int i = 0; i < Value.TYPE_COUNT; i++) { for (int i = 0; i < Value.TYPE_COUNT; i++) {
Value v = create(i);
String s = "type: " + v.getType() + " calculated: " + v.getMemory() + " real: " + MemoryFootprint.getObjectSize(v) + " " + v.getClass().getName() + ": " + v.toString();
trace(s);
}
for (int i = 0; i < Value.TYPE_COUNT; i++) {
assertEquals(i, create(i).getType());
testType(i); testType(i);
} }
} }
...@@ -83,7 +96,7 @@ public class TestValueMemory extends TestBase implements DataHandler { ...@@ -83,7 +96,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
long memory = 0; long memory = 0;
for (int i = 0; memory < 1000000; i++) { for (int i = 0; memory < 1000000; i++) {
Value v = create(type); Value v = create(type);
memory += v.getMemory(); memory += v.getMemory() + Constants.MEMORY_POINTER;
list.add(v); list.add(v);
} }
Object[] array = list.toArray(); Object[] array = list.toArray();
...@@ -99,8 +112,11 @@ public class TestValueMemory extends TestBase implements DataHandler { ...@@ -99,8 +112,11 @@ public class TestValueMemory extends TestBase implements DataHandler {
System.gc(); System.gc();
long used = Utils.getMemoryUsed() - first; long used = Utils.getMemoryUsed() - first;
memory /= 1024; memory /= 1024;
if (config.traceTest) {
trace("Type: " + type + " Used memory: " + used + " calculated: " + memory + " length: " + array.length + " size: " + size);
}
if (used > memory * 3) { if (used > memory * 3) {
fail("Type: " + type + " Used memory: " + used + " calculated: " + memory + " " + array.length + " size: " + size); fail("Type: " + type + " Used memory: " + used + " calculated: " + memory + " length: " + array.length + " size: " + size);
} }
} }
private Value create(int type) throws SQLException { private Value create(int type) throws SQLException {
...@@ -155,8 +171,7 @@ public class TestValueMemory extends TestBase implements DataHandler { ...@@ -155,8 +171,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
return ValueArray.get(list); return ValueArray.get(list);
} }
case Value.RESULT_SET: case Value.RESULT_SET:
// not supported currently return ValueResultSet.get(new SimpleResultSet());
return ValueNull.INSTANCE;
case Value.JAVA_OBJECT: case Value.JAVA_OBJECT:
return ValueJavaObject.getNoCopy(randomBytes(random.nextInt(100))); return ValueJavaObject.getNoCopy(randomBytes(random.nextInt(100)));
case Value.UUID: case Value.UUID:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论