This library wraps up low level access to
-
Maths Functions for casting types, rounding double, faster hashing.
-
Histogram A high performance wide range histogram.
-
JLBH Java Latency Benchmarking Harness.
The Jvm class in its static initialiser will load system properties from a file called system.properties
if it can find this file in the current directory. If it can’t find this file in current directory it will
look in ..
too.
This is a useful feature if you want to de-clutter your command line.
Chronicle Core class Jvm
loads the file system.properties
into the System’s properties.
To ensure it is loaded early enough, add the following code to your Main
static {
Jvm.init();
}
The choice of file to load can be overridden on the command line with -Dsystem.properties=my.properties
See Jvm.Main
in the src/test
as an example.
Note
|
If you specify system properties on the command line then they will override those in the
system.properties file.
|
This allows you to access native memory using primitives and some thread safe operations.
Memory memory = OS.memory();
long address = memory.allocate(1024);
try {
memory.writeInt(address, 1);
assert memory.readInt(address) == 1;
final boolean swapped = memory.compareAndSwapInt(address, 1, 2);
assert swapped;
assert memory.readInt(address) == 2;
} finally {
memory.freeMemory(address, 1024);
}
Check the JVM is running in debug mode
if (Jvm.isDebug()) {
// running in debug.
Rethrow a checked exception as an unchecked one.
try {
// IO operation
} catch (IOException ioe) {
throw Jvm.rethrow(ioe);
}
Get a Field for a Class by name
Field theUnsafe = Jvm.getField(Unsafe.class, "theUnsafe");
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Access to system calls
int processId = OS.getProcessId();
int maxProcessId = OS.getMaxProcessId();
int pageSize = OS.getPageSize();
boolean isWindows = OS.isWindows();
boolean is64bit = OS.is64Bit();
String hostname = OS.getHostName();
String username = OS.getUserName();
String targetDir = OS.getTarget(); // where is the target directory during builds.
Memory mapped files
FileChannel fc = new CleaningRandomAccessFile(fileName, "rw").getChannel();
// map in 64 KiB
long address = OS.map(fc, MapMode.READ_WRITE, 0, 64 << 10);
// use address
OS.memory().writeLong(1024L, 0x1234567890ABCDEFL);
// unmap memory region
OS.unmap(address, 64 << 10);
Component which are closeable or reference counted can be released deterministically without waiting for a GC.
A Closeable
resources has a simple lifecycle.
It is open when created, and cannot be used once closed.
public class AbstractCloseableTest {
@Test
public void close() {
MyCloseable mc = new MyCloseable();
assertFalse(mc.isClosed());
assertEquals(0, mc.performClose);
mc.throwExceptionIfClosed();
mc.close();
assertTrue(mc.isClosed());
assertEquals(1, mc.performClose);
mc.close();
assertTrue(mc.isClosed());
assertEquals(1, mc.performClose);
}
@Test(expected = IllegalStateException.class)
public void throwExceptionIfClosed() {
MyCloseable mc = new MyCloseable();
mc.close();
mc.throwExceptionIfClosed();
}
@Test
public void warnAndCloseIfNotClosed() {
Map<ExceptionKey, Integer> map = Jvm.recordExceptions();
MyCloseable mc = new MyCloseable();
mc.warnAndCloseIfNotClosed();
Jvm.resetExceptionHandlers();
assertEquals("Discarded without closing\n" +
"java.lang.IllegalStateException: net.openhft.chronicle.core.StackTrace: Created Here",
map.keySet().stream()
.map(e -> e.message + "\n" + e.throwable)
.collect(Collectors.joining(", ")));
}
static class MyCloseable extends AbstractCloseable {
int performClose;
@Override
protected void performClose() {
performClose++;
}
}
}
Use reference counting to deterministically release resources.
A reference counted resource can add reservations until closed.
public class AbstractReferenceCountedTest {
@Test
public void reserve() {
assertTrue(Jvm.isResourceTracing());
MyReferenceCounted rc = new MyReferenceCounted();
assertEquals(1, rc.refCount());
ReferenceOwner a = ReferenceOwner.temporary("a");
rc.reserve(a);
assertEquals(2, rc.refCount());
ReferenceOwner b = ReferenceOwner.temporary("b");
rc.reserve(b);
assertEquals(3, rc.refCount());
try {
rc.reserve(a);
fail();
} catch (IllegalStateException ignored) {
}
assertEquals(3, rc.refCount());
rc.release(b);
assertEquals(2, rc.refCount());
rc.release(a);
assertEquals(1, rc.refCount());
assertEquals(0, rc.performRelease);
rc.releaseLast();
assertEquals(0, rc.refCount());
assertEquals(1, rc.performRelease);
}
@Test
public void reserveWhenClosed() {
MyReferenceCounted rc = new MyReferenceCounted();
assertEquals(1, rc.refCount());
ReferenceOwner a = ReferenceOwner.temporary("a");
rc.reserve(a);
assertEquals(2, rc.refCount());
assertFalse(rc.isClosed());
rc.closeable.close();
assertEquals(2, rc.refCount());
assertTrue(rc.isClosed());
ReferenceOwner b = ReferenceOwner.temporary("b");
try {
rc.reserve(b);
fail();
} catch (IllegalStateException ignored) {
}
assertEquals(2, rc.refCount());
assertFalse(rc.tryReserve(b));
assertEquals(2, rc.refCount());
rc.release(a);
assertEquals(1, rc.refCount());
assertEquals(0, rc.performRelease);
rc.throwExceptionIfReleased();
rc.releaseLast();
assertEquals(0, rc.refCount());
assertEquals(1, rc.performRelease);
rc.throwExceptionBadResourceOwner();
try {
rc.throwExceptionIfClosed();
fail();
} catch (IllegalStateException ignored) {
}
try {
rc.throwExceptionIfReleased();
fail();
} catch (IllegalStateException ignored) {
}
}
@Test
public void throwExceptionBadResourceOwner() {
MyReferenceCounted rc = new MyReferenceCounted();
MyReferenceCounted rc2 = new MyReferenceCounted();
rc.reserve(rc2);
rc.throwExceptionBadResourceOwner();
rc2.closeable.close();
try {
rc.throwExceptionBadResourceOwner();
fail();
} catch (IllegalStateException ignored) {
}
rc.release(rc2);
rc.releaseLast();
}
@Test
public void throwExceptionIfClosed() {
MyReferenceCounted rc = new MyReferenceCounted();
rc.throwExceptionIfClosed();
rc.closeable.close();
try {
rc.throwExceptionIfClosed();
fail();
} catch (IllegalStateException ignored) {
}
}
static class MyReferenceCounted extends AbstractReferenceCounted {
final AbstractCloseable closeable;
int performRelease;
public MyReferenceCounted() {
this(new AbstractCloseableTest.MyCloseable());
}
public MyReferenceCounted(AbstractCloseable abstractCloseable) {
super(abstractCloseable);
closeable = abstractCloseable;
}
@Override
protected void performRelease() {
performRelease++;
}
}
}
MappedFile mf = MappedFile.mappedFile(tmp, chunkSize, 0);
MappedBytesStore bs = mf.acquireByteStore(chunkSize + (1 << 10));
assertEquals(2, mf.refCount());
assertEquals(3, bs.refCount());
assertEquals("refCount: 2, 0, 3", mf.referenceCounts());
mf.close();
assertEquals(2, bs.refCount());
assertEquals("refCount: 1, 0, 2", mf.referenceCounts());
bs2.releaseLast();
assertEquals(1, mf.refCount());
assertEquals(1, bs.refCount());
bs.releaseLast();
assertEquals(0, bs.refCount());
assertEquals(0, mf.refCount());
assertEquals("refCount: 0, 0, 0", mf.referenceCounts());
There is String and Enum object pools to turn a CharSequence into a String.
Bytes b = Bytes.from("Hello World");
b.readSkip(6);
StringInterner si = new StringInterner(128);
String s = si.intern(b);
String s2 = si.intern(b);
assertEquals("World", s);
assertSame(s, s2);
Add caching of a data structure for each class using a lambda
public static final ClassLocal<EnumInterner> ENUM_INTERNER =
ClassLocal.withInitial(c -> new EnumInterner<>(c));
E enumValue = ENUM_INTERNER.get(enumClass).intern(stringBuilder);
Maths functions to support rounds
double a = 0.1;
double b = 0.3;
double c= Maths.round2(b - a); // 0.2 rounded to 2 decimal places
Checking type conversions
int i = Maths.toInt32(longValue);
There is a number of FunctionalInterfaces you can utilise as method arguments. This allows implicitly making a lambda Serializable.
// in KeyedVisitable
default <R> R applyToKey(K key, @NotNull SerializableFunction<E, R> function) {
// in code
String fullename = map.applyToKey("u:123223", u -> u.getFullName());
A high dynamic range histogram with tunable accuracy.
Histogram h = new Histogram(32, 4);
long start = instance.ticks(), prev = start;
for (int i = 0; i <= 1000_000_000; i++) {
long now = instance.ticks();
long time = now - prev;
h.sample(time);
prev = now;
}
System.out.println(h.toLongMicrosFormat(instance::toMicros));
JLBH has moved home and now lives in its own project, see JLBH.