Skip to content

MVStore OOM as the amount of data increases and use the file based store #3618

@forchid

Description

@forchid

OS windows 10, java 8, and h2-2.1.214.jar.

The test case

The standalone test program.

import org.h2.mvstore.*;

import java.nio.charset.*;
import java.util.*;
import java.text.*;
import java.io.*;

public class CrudTest {
	
	static final Charset charset = StandardCharsets.UTF_8;
	static final int o = Integer.getInteger("o", 0);           // Offset
	static final int n = Integer.getInteger("n", 10_000_000);  // Limit
	static final int p = Integer.getInteger("p", 10_000);      // Print and commit
	static final String op = System.getProperty("op", "q");    // operation
	static final boolean nul = Boolean.getBoolean("null-value");
	static final int newDelta = Integer.getInteger("new-value-delta", 0);
	static final int oldDelta = Integer.getInteger("old-value-delta", 0);
	static final int cacheSize = Integer.getInteger("cache-size", 16);
	static final int threads = Integer.getInteger("threads", 10);
	
	public static void main(String[] args) throws InterruptedException {
		long s = System.currentTimeMillis();
		String mainName = Thread.currentThread().getName();
		String fileName = String.format("tmp%sCrudTest.mv", File.separator);
		DateFormat tf = new SimpleDateFormat("HH:mm:ss.SSS");
		System.out.printf("%s[%s] open store ...%n", tf.format(new Date()), mainName);
		MVStore store = new MVStore.Builder()
		.fileName(fileName)
		.cacheSize(cacheSize/*MB*/)
		.open();
		System.out.printf("%s[%s] open store OK%n", tf.format(new Date()), mainName);
		
		Thread[] workers = new Thread[threads];
		int limitPerWorker = (n - o) / threads;
		try {
			Map<String, Double> account = store.openMap("account");
			for (int wi = 0; wi < threads; ++wi) {
				int offset = limitPerWorker * wi;
				int limit;
				if (wi == threads - 1) limit = n;
				else limit  = offset + limitPerWorker;
				
				workers[wi] = new Thread(() -> {
					long ts = System.currentTimeMillis();
					DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
					String thrName = Thread.currentThread().getName();
					int i = offset;
					try {
						System.out.printf("%s[%s] op %s, range %d -> %d%n", 
							df.format(new Date()), thrName, op, offset, limit);
						switch(op) {
							case "i":
							case "u":
								for (; i < limit; ++i) {
									String name = "acc-" + i;
									double balance = i % 10000;
									// add/update the key-value pair to the store.
									Double old = account.put(name, balance + newDelta);
									if (i % p == 0) {
										store.commit();
										System.out.printf("%s[%s] op %s, commit at %d%n", 
											df.format(new Date()), thrName, op, i);
									}
									if (nul) {
										if (old != null) throw new AssertionError(name + "'s exists: " + old);
									} else if (old == null || old != balance + oldDelta) {
										throw new AssertionError(name + "'s balance error: old = " + old);
									}
								}
							break;
						case "d":
							for (; i < limit; ++i) {
								String name = "acc-" + i;
								double balance = i % 10000;
								Double old = account.remove(name);
								if (i % p == 0) {
									store.commit();
									System.out.printf("%s[%s] op %s, commit at %d%n", 
										df.format(new Date()), thrName, op, i);
								}
								if (nul) {
									if (old != null) throw new AssertionError(name + "'s exists: " + old);
								} else if (old == null || old != balance + oldDelta) {
									throw new AssertionError(name + "'s balance error: old = " + old);
								}
							}
							break;
						default:
							for (; i < limit; ++i) {
								String name = "acc-" + i;
								double balance = i % 10000;
								Double old = account.get(name);
								if (i % p == 0) {
									System.out.printf("%s[%s] op %s, i %d%n", 
										df.format(new Date()), thrName, op, i);
								}
								if (nul) {
									if (old != null) throw new AssertionError(name + "'s exists: " + old);
								} else if (old == null || old != balance + oldDelta) {
									throw new AssertionError(name + "'s balance error: old = " + old);
								}
							}
							break;
						}
					} finally {
						long te = System.currentTimeMillis();
						System.out.printf("%s[%s] op %s, null-value %s, i %d, items %d, time %dms%n", 
							df.format(new Date()), thrName, op, nul, i, n, te - ts);
					}
				}, "worker-"+wi);
				workers[wi].start();
			} // for-threads
		} finally {
			for (int i = 0; i < threads; ++i) {
				workers[i].join();
			}
			store.close();
			long e = System.currentTimeMillis();
			System.out.printf("%s[%s] op %s, threads %s, null-value %s, items %d, time %dms%n", 
				tf.format(new Date()), mainName, op, threads, nul, n, e - s);
		}
	}
	
}

These test data and result.

  1. JVM heap 64MB, and data items 0.1 billion.
>java -Xmx64m -Dn=100000000 -Dop=i -Dnull-value=true CrudTest
18:49:54.395: op i, commit at 86960000
18:49:55.066: op i, commit at 86970000
18:49:55.840: op i, commit at 86980000
18:49:56.732: op i, commit at 86990000
18:49:57.685: op i, commit at 87000000
18:49:58.951: op i, commit at 87010000
18:49:59.795: op i, commit at 87020000
Op i, null-value true, i 87030000, items 100000000, time 391480ms
Exception in thread "main" org.h2.mvstore.MVStoreException: java.util.concurrent.ExecutionException: org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Java heap space [2.1.214/3] [2.1.214/3]
        at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:1004)
        at org.h2.mvstore.MVStore.storeNow(MVStore.java:1529)
        at org.h2.mvstore.MVStore.store(MVStore.java:1496)
        at org.h2.mvstore.MVStore.commit(MVStore.java:1469)
        at org.h2.mvstore.MVStore.commit(MVStore.java:1458)
        at CrudTest.main(CrudTest.java:33)
Caused by: java.util.concurrent.ExecutionException: org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Java heap space [2.1.214/3]
        at java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.util.concurrent.FutureTask.get(FutureTask.java:192)
        at org.h2.mvstore.MVStore.submitOrRun(MVStore.java:1541)
        at org.h2.mvstore.MVStore.storeNow(MVStore.java:1517)
        ... 4 more
  1. JVM heap 128MB, and the data items 1 billion.
>java -Xmx128m -Do=100000001 -Dn=900000000 -Dop=i -Dnull-value=true CrudTest
20:16:35.150: op i, commit at 326590000
20:16:36.228: op i, commit at 326600000
20:16:37.307: op i, commit at 326610000
Op i, null-value true, i 326620000, items 900000000, time 1223004ms
Exception in thread "main" org.h2.mvstore.MVStoreException: java.util.concurrent.ExecutionException: org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Java heap space [2.1.214/3] [2.1.214/3]
        at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:1004)
        at org.h2.mvstore.MVStore.storeNow(MVStore.java:1529)
        at org.h2.mvstore.MVStore.store(MVStore.java:1496)
        at org.h2.mvstore.MVStore.commit(MVStore.java:1469)
        at org.h2.mvstore.MVStore.commit(MVStore.java:1458)
        at CrudTest.main(CrudTest.java:37)
Caused by: java.util.concurrent.ExecutionException: org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Java heap space [2.1.214/3]
        at java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.util.concurrent.FutureTask.get(FutureTask.java:192)
        at org.h2.mvstore.MVStore.submitOrRun(MVStore.java:1541)
        at org.h2.mvstore.MVStore.storeNow(MVStore.java:1517)
        ... 4 more
Caused by: org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Java heap space [2.1.214/3]
        at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:1004)
        at org.h2.mvstore.MVStore.serializeAndStore(MVStore.java:1605)
        at org.h2.mvstore.MVStore.lambda$storeNow$4(MVStore.java:1518)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:750)
  1. JVM heap 1024MB, and the data items 2 billion.
>java -Xmx1024m -Do=0 -Dn=2000000000 -Dop=i -Dnull-value=true -Dthreads=10 CrudTest
23:39:27.858[worker-0] op i, commit at 105310000
23:39:29.267[worker-0] op i, commit at 105320000
23:39:29.267[worker-0] op i, null-value true, i 105320001, items 2000000000, time 3669314ms
23:39:29.267[main] op i, threads 10, null-value true, items 2000000000, time 3669457ms
Exception in thread "worker-0" org.h2.mvstore.MVStoreException: Map account(2) is closed. org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Capacity: 3145728 [2.1.214/3] [2.1.214/4]
        at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:1004)
        at org.h2.mvstore.MVMap.beforeWrite(MVMap.java:962)
        at org.h2.mvstore.MVMap.operate(MVMap.java:1757)
        at org.h2.mvstore.MVMap.put(MVMap.java:156)
        at CrudTest.lambda$main$0(CrudTest.java:58)
        at java.lang.Thread.run(Thread.java:750)
Caused by: org.h2.mvstore.MVStoreException: java.lang.OutOfMemoryError: Capacity: 3145728 [2.1.214/3]
        at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:1004)
        at org.h2.mvstore.MVStore.serializeAndStore(MVStore.java:1605)
        at org.h2.mvstore.MVStore.lambda$storeNow$4(MVStore.java:1518)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        ... 1 more
Caused by: java.lang.OutOfMemoryError: Capacity: 3145728
        at org.h2.mvstore.WriteBuffer.grow(WriteBuffer.java:322)
        at org.h2.mvstore.WriteBuffer.ensureCapacity(WriteBuffer.java:302)
        at org.h2.mvstore.WriteBuffer.putStringData(WriteBuffer.java:75)
        at org.h2.mvstore.type.ObjectDataType$StringType.write(ObjectDataType.java:1160)
        at org.h2.mvstore.type.ObjectDataType$StringType.write(ObjectDataType.java:1126)
        at org.h2.mvstore.type.ObjectDataType.write(ObjectDataType.java:142)
        at org.h2.mvstore.type.BasicDataType.write(BasicDataType.java:67)
        at org.h2.mvstore.Page.write(Page.java:720)
        at org.h2.mvstore.Page$Leaf.writeUnsavedRecursive(Page.java:1618)
        at org.h2.mvstore.Page$NonLeaf.writeChildrenRecursive(Page.java:1344)
        at org.h2.mvstore.Page$NonLeaf.writeUnsavedRecursive(Page.java:1330)
        at org.h2.mvstore.Page$NonLeaf.writeChildrenRecursive(Page.java:1344)
        at org.h2.mvstore.Page$NonLeaf.writeUnsavedRecursive(Page.java:1330)
        at org.h2.mvstore.Page$NonLeaf.writeChildrenRecursive(Page.java:1344)
        at org.h2.mvstore.Page$NonLeaf.writeUnsavedRecursive(Page.java:1330)
        at org.h2.mvstore.Page$NonLeaf.writeChildrenRecursive(Page.java:1344)
        at org.h2.mvstore.Page$NonLeaf.writeUnsavedRecursive(Page.java:1330)
        at org.h2.mvstore.Page$NonLeaf.writeChildrenRecursive(Page.java:1344)
        at org.h2.mvstore.Page$NonLeaf.writeUnsavedRecursive(Page.java:1330)
        at org.h2.mvstore.Page$NonLeaf.writeChildrenRecursive(Page.java:1344)
        at org.h2.mvstore.Page$NonLeaf.writeUnsavedRecursive(Page.java:1330)
        at org.h2.mvstore.MVStore.serializeToBuffer(MVStore.java:1669)
        at org.h2.mvstore.MVStore.serializeAndStore(MVStore.java:1598)
        ... 6 more

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions