diff --git a/community/common/src/main/java/org/neo4j/memory/GlobalMemoryTracker.java b/community/common/src/main/java/org/neo4j/memory/GlobalMemoryTracker.java new file mode 100644 index 0000000000000..c1e578f03bfaa --- /dev/null +++ b/community/common/src/main/java/org/neo4j/memory/GlobalMemoryTracker.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +import java.util.concurrent.atomic.LongAdder; + +/** + * Global memory tracker that can be used in a global multi threaded context to record + * allocation and de-allocation of native memory. + * @see org.neo4j.memory.MemoryAllocationTracker + * @see MemoryTracker + */ +public class GlobalMemoryTracker implements MemoryTracker, MemoryAllocationTracker +{ + private LongAdder longAdder = new LongAdder(); + + public static final GlobalMemoryTracker INSTANCE = new GlobalMemoryTracker(); + + private GlobalMemoryTracker() + { + } + + @Override + public long usedDirectMemory() + { + return longAdder.sum(); + } + + @Override + public void allocate( long allocatedBytes ) + { + longAdder.add( allocatedBytes ); + } + + @Override + public void deallocate( long deAllocatedBytes ) + { + longAdder.add( -deAllocatedBytes ); + } +} diff --git a/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java b/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java new file mode 100644 index 0000000000000..91755b39bd6e2 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +/** + * Memory allocation tracker that can be used in local context that required + * tracking of memory that is independent from global. + * All allocations/de-allocation reported to this trackers also will be reported to global tracker transparently. + */ +public class LocalMemoryTracker implements MemoryTracker, MemoryAllocationTracker +{ + private long allocatedBytes; + private GlobalMemoryTracker globalTracker = GlobalMemoryTracker.INSTANCE; + + @Override + public void allocate( long allocatedBytes ) + { + globalTracker.allocate( allocatedBytes ); + this.allocatedBytes += allocatedBytes; + } + + @Override + public void deallocate( long deallocatedBytes ) + { + globalTracker.deallocate( deallocatedBytes ); + this.allocatedBytes -= deallocatedBytes; + } + + /** + * @return number of locally used bytes. + */ + @Override + public long usedDirectMemory() + { + return allocatedBytes; + } +} diff --git a/community/common/src/main/java/org/neo4j/memory/MemoryAllocationTracker.java b/community/common/src/main/java/org/neo4j/memory/MemoryAllocationTracker.java new file mode 100644 index 0000000000000..a83651d250a05 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/memory/MemoryAllocationTracker.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +/** + * Memory allocation tracker that tracks bytes allocation and de-allocation + */ +public interface MemoryAllocationTracker +{ + /** + * Record allocation of bytes + * @param allocatedBytes number of allocated bytes + */ + void allocate( long allocatedBytes ); + + /** + * Record de-allocation of bytes + * @param deAllocatedBytes number of de0allocated bytes + */ + void deallocate( long deAllocatedBytes ); +} diff --git a/community/common/src/main/java/org/neo4j/memory/MemoryTracker.java b/community/common/src/main/java/org/neo4j/memory/MemoryTracker.java new file mode 100644 index 0000000000000..c5710f1b7cb34 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/memory/MemoryTracker.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +/** + * Tracker that capable to report number of allocated bytes. + * @see MemoryAllocationTracker + */ +public interface MemoryTracker +{ + /** + * @return number of bytes of direct memory that are used + */ + long usedDirectMemory(); +} diff --git a/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java b/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java new file mode 100644 index 0000000000000..4b7933a33ca23 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class GlobalMemoryTrackerTest +{ + + @Test + public void trackMemoryAllocations() + { + long initialUsedMemory = GlobalMemoryTracker.INSTANCE.usedDirectMemory(); + GlobalMemoryTracker.INSTANCE.allocate( 10 ); + GlobalMemoryTracker.INSTANCE.allocate( 20 ); + GlobalMemoryTracker.INSTANCE.allocate( 40 ); + assertEquals( 70, GlobalMemoryTracker.INSTANCE.usedDirectMemory() - initialUsedMemory ); + } + + @Test + public void trackMemoryDeallocations() + { + long initialUsedMemory = GlobalMemoryTracker.INSTANCE.usedDirectMemory(); + GlobalMemoryTracker.INSTANCE.allocate( 100 ); + assertEquals( 100, GlobalMemoryTracker.INSTANCE.usedDirectMemory() - initialUsedMemory ); + + GlobalMemoryTracker.INSTANCE.deallocate( 20 ); + assertEquals( 80, GlobalMemoryTracker.INSTANCE.usedDirectMemory() - initialUsedMemory ); + + GlobalMemoryTracker.INSTANCE.deallocate( 40 ); + assertEquals( 40, GlobalMemoryTracker.INSTANCE.usedDirectMemory() - initialUsedMemory ); + } +} diff --git a/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java b/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java new file mode 100644 index 0000000000000..68148778025a0 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class LocalMemoryTrackerTest +{ + @Test + public void trackMemoryAllocations() + { + LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); + memoryTracker.allocate( 10 ); + memoryTracker.allocate( 20 ); + memoryTracker.allocate( 40 ); + assertEquals( 70, memoryTracker.usedDirectMemory()); + } + + @Test + public void trackMemoryDeallocations() + { + LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); + memoryTracker.allocate( 100 ); + assertEquals( 100, memoryTracker.usedDirectMemory() ); + + memoryTracker.deallocate( 20 ); + assertEquals( 80, memoryTracker.usedDirectMemory() ); + + memoryTracker.deallocate( 40 ); + assertEquals( 40, memoryTracker.usedDirectMemory() ); + } + + @Test + public void localMemoryTrackerPropagatesAllocationsToGlobalTracker() + { + GlobalMemoryTracker globalMemoryTracker = GlobalMemoryTracker.INSTANCE; + long initialGlobalUsage = globalMemoryTracker.usedDirectMemory(); + LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); + + memoryTracker.allocate( 100 ); + assertEquals( 100, memoryTracker.usedDirectMemory() ); + assertEquals( 100, globalMemoryTracker.usedDirectMemory() - initialGlobalUsage ); + + memoryTracker.deallocate( 50 ); + assertEquals( 50, memoryTracker.usedDirectMemory() ); + assertEquals( 50, globalMemoryTracker.usedDirectMemory() - initialGlobalUsage ); + } + +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/ResourceTypesIT.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/ResourceTypesIT.java index 0ab06fc871b35..590493af02eed 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/ResourceTypesIT.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/ResourceTypesIT.java @@ -23,6 +23,7 @@ import org.neo4j.collection.primitive.Primitive; import org.neo4j.collection.primitive.PrimitiveLongLongMap; +import org.neo4j.memory.GlobalMemoryTracker; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @@ -40,7 +41,7 @@ public class ResourceTypesIT public void indexEntryHashing() { int collisions = 0; - try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap( 35_000_000 ) ) + try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap( 35_000_000, GlobalMemoryTracker.INSTANCE ) ) { int labelIdCount = 50; diff --git a/community/kernel/src/test/java/org/neo4j/test/FakeCpuClock.java b/community/kernel/src/test/java/org/neo4j/test/FakeCpuClock.java index 71c8cb6041bbb..42d186b2436a3 100644 --- a/community/kernel/src/test/java/org/neo4j/test/FakeCpuClock.java +++ b/community/kernel/src/test/java/org/neo4j/test/FakeCpuClock.java @@ -19,14 +19,15 @@ */ package org.neo4j.test; -import java.util.concurrent.TimeUnit; - import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import java.util.concurrent.TimeUnit; + import org.neo4j.collection.primitive.Primitive; import org.neo4j.collection.primitive.PrimitiveLongLongMap; +import org.neo4j.memory.GlobalMemoryTracker; import org.neo4j.resources.CpuClock; public class FakeCpuClock extends CpuClock implements TestRule @@ -39,7 +40,7 @@ public long cpuTimeNanos( long threadId ) return -1; } }; - private final PrimitiveLongLongMap cpuTimes = Primitive.offHeapLongLongMap(); + private final PrimitiveLongLongMap cpuTimes = Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ); @Override public long cpuTimeNanos( long threadId ) diff --git a/community/kernel/src/test/java/org/neo4j/test/FakeHeapAllocation.java b/community/kernel/src/test/java/org/neo4j/test/FakeHeapAllocation.java index 69eee9dd92565..c5a6776fcfb3b 100644 --- a/community/kernel/src/test/java/org/neo4j/test/FakeHeapAllocation.java +++ b/community/kernel/src/test/java/org/neo4j/test/FakeHeapAllocation.java @@ -24,6 +24,7 @@ import org.junit.runners.model.Statement; import org.neo4j.collection.primitive.PrimitiveLongLongMap; +import org.neo4j.memory.GlobalMemoryTracker; import org.neo4j.resources.HeapAllocation; import static java.lang.Thread.currentThread; @@ -31,7 +32,7 @@ public class FakeHeapAllocation extends HeapAllocation implements TestRule { - private final PrimitiveLongLongMap allocation = offHeapLongLongMap(); + private final PrimitiveLongLongMap allocation = offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ); @Override public long allocatedBytes( long threadId ) diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/Primitive.java b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/Primitive.java index d1dee17285fe4..767eb00572c8e 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/Primitive.java +++ b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/Primitive.java @@ -36,6 +36,7 @@ import org.neo4j.collection.primitive.hopscotch.PrimitiveLongIntHashMap; import org.neo4j.collection.primitive.hopscotch.PrimitiveLongLongHashMap; import org.neo4j.collection.primitive.hopscotch.PrimitiveLongObjectHashMap; +import org.neo4j.memory.MemoryAllocationTracker; import static org.neo4j.collection.primitive.hopscotch.HopScotchHashingAlgorithm.NO_MONITOR; @@ -82,14 +83,14 @@ public static PrimitiveLongSet longSet( int initialCapacity ) VALUE_MARKER, NO_MONITOR ); } - public static PrimitiveLongSet offHeapLongSet() + public static PrimitiveLongSet offHeapLongSet( MemoryAllocationTracker allocationTracker ) { - return offHeapLongSet( DEFAULT_OFFHEAP_CAPACITY ); + return offHeapLongSet( DEFAULT_OFFHEAP_CAPACITY, allocationTracker ); } - public static PrimitiveLongSet offHeapLongSet( int initialCapacity ) + public static PrimitiveLongSet offHeapLongSet( int initialCapacity, MemoryAllocationTracker allocationTracker ) { - return new PrimitiveLongHashSet( new LongKeyUnsafeTable<>( initialCapacity, VALUE_MARKER ), + return new PrimitiveLongHashSet( new LongKeyUnsafeTable<>( initialCapacity, VALUE_MARKER, allocationTracker ), VALUE_MARKER, NO_MONITOR ); } @@ -113,14 +114,14 @@ public static PrimitiveLongLongMap longLongMap( int initialCapacity ) return new PrimitiveLongLongHashMap( new LongKeyLongValueTable( initialCapacity ), NO_MONITOR ); } - public static PrimitiveLongLongMap offHeapLongLongMap() + public static PrimitiveLongLongMap offHeapLongLongMap( MemoryAllocationTracker allocationTracker ) { - return offHeapLongLongMap( DEFAULT_OFFHEAP_CAPACITY ); + return offHeapLongLongMap( DEFAULT_OFFHEAP_CAPACITY, allocationTracker ); } - public static PrimitiveLongLongMap offHeapLongLongMap( int initialCapacity ) + public static PrimitiveLongLongMap offHeapLongLongMap( int initialCapacity, MemoryAllocationTracker allocationTracker ) { - return new PrimitiveLongLongHashMap( new LongKeyLongValueUnsafeTable( initialCapacity ), NO_MONITOR ); + return new PrimitiveLongLongHashMap( new LongKeyLongValueUnsafeTable( initialCapacity, allocationTracker ), NO_MONITOR ); } public static PrimitiveLongObjectMap longObjectMap() @@ -144,15 +145,15 @@ public static PrimitiveIntSet intSet( int initialCapacity ) VALUE_MARKER, NO_MONITOR ); } - public static PrimitiveIntSet offHeapIntSet() + public static PrimitiveIntSet offHeapIntSet( MemoryAllocationTracker allocationTracker ) { - return new PrimitiveIntHashSet( new IntKeyUnsafeTable<>( 1 << 20, VALUE_MARKER ), + return new PrimitiveIntHashSet( new IntKeyUnsafeTable<>( 1 << 20, VALUE_MARKER, allocationTracker ), VALUE_MARKER, NO_MONITOR ); } - public static PrimitiveIntSet offHeapIntSet( int initialCapacity ) + public static PrimitiveIntSet offHeapIntSet( int initialCapacity, MemoryAllocationTracker allocationTracker ) { - return new PrimitiveIntHashSet( new IntKeyUnsafeTable<>( initialCapacity, VALUE_MARKER ), + return new PrimitiveIntHashSet( new IntKeyUnsafeTable<>( initialCapacity, VALUE_MARKER, allocationTracker ), VALUE_MARKER, NO_MONITOR ); } diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyUnsafeTable.java b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyUnsafeTable.java index 7e5eb030fc899..3f2f51b0f12c2 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyUnsafeTable.java +++ b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyUnsafeTable.java @@ -19,13 +19,14 @@ */ package org.neo4j.collection.primitive.hopscotch; +import org.neo4j.memory.MemoryAllocationTracker; import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil; public class IntKeyUnsafeTable extends UnsafeTable { - public IntKeyUnsafeTable( int capacity, VALUE valueMarker ) + public IntKeyUnsafeTable( int capacity, VALUE valueMarker, MemoryAllocationTracker allocationTracker ) { - super( capacity, 4, valueMarker ); + super( capacity, 4, valueMarker, allocationTracker ); } @Override @@ -46,6 +47,6 @@ protected void internalPut( long keyAddress, long key, VALUE value ) @Override protected Table newInstance( int newCapacity ) { - return new IntKeyUnsafeTable<>( newCapacity, valueMarker ); + return new IntKeyUnsafeTable<>( newCapacity, valueMarker, allocationTracker ); } } diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyLongValueUnsafeTable.java b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyLongValueUnsafeTable.java index 4173f7224b823..44e32c817e3e2 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyLongValueUnsafeTable.java +++ b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyLongValueUnsafeTable.java @@ -19,11 +19,13 @@ */ package org.neo4j.collection.primitive.hopscotch; +import org.neo4j.memory.MemoryAllocationTracker; + public class LongKeyLongValueUnsafeTable extends UnsafeTable { - public LongKeyLongValueUnsafeTable( int capacity ) + public LongKeyLongValueUnsafeTable( int capacity, MemoryAllocationTracker allocationTracker ) { - super( capacity, 16, new long[1] ); + super( capacity, 16, new long[1], allocationTracker ); } @Override @@ -77,6 +79,6 @@ public long[] value( int index ) @Override protected Table newInstance( int newCapacity ) { - return new LongKeyLongValueUnsafeTable( newCapacity ); + return new LongKeyLongValueUnsafeTable( newCapacity, allocationTracker ); } } diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyUnsafeTable.java b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyUnsafeTable.java index a8e9fc8df4ba9..7de5cbb2f480f 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyUnsafeTable.java +++ b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyUnsafeTable.java @@ -19,11 +19,13 @@ */ package org.neo4j.collection.primitive.hopscotch; +import org.neo4j.memory.MemoryAllocationTracker; + public class LongKeyUnsafeTable extends UnsafeTable { - public LongKeyUnsafeTable( int capacity, VALUE valueMarker ) + public LongKeyUnsafeTable( int capacity, VALUE valueMarker, MemoryAllocationTracker allocationTracker ) { - super( capacity, 8, valueMarker ); + super( capacity, 8, valueMarker, allocationTracker ); } @Override @@ -41,6 +43,6 @@ protected void internalPut( long keyAddress, long key, VALUE value ) @Override protected Table newInstance( int newCapacity ) { - return new LongKeyUnsafeTable<>( newCapacity, valueMarker ); + return new LongKeyUnsafeTable<>( newCapacity, valueMarker, allocationTracker ); } } diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/UnsafeTable.java b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/UnsafeTable.java index 641ebd60bf7c2..24eceeff54574 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/UnsafeTable.java +++ b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/hopscotch/UnsafeTable.java @@ -19,6 +19,7 @@ */ package org.neo4j.collection.primitive.hopscotch; +import org.neo4j.memory.MemoryAllocationTracker; import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil; public abstract class UnsafeTable extends PowerOfTwoQuantizedTable @@ -26,16 +27,19 @@ public abstract class UnsafeTable extends PowerOfTwoQuantizedTable private final int bytesPerKey; private final int bytesPerEntry; private final long dataSize; + private final long bytesToAllocate; // address which should be free when closing private final long allocatedAddress; // address which should be used to access the table, the address where the table actually starts at private final long address; protected final VALUE valueMarker; + protected final MemoryAllocationTracker allocationTracker; - protected UnsafeTable( int capacity, int bytesPerKey, VALUE valueMarker ) + protected UnsafeTable( int capacity, int bytesPerKey, VALUE valueMarker, MemoryAllocationTracker allocationTracker ) { super( capacity, 32 ); UnsafeUtil.assertHasUnsafe(); + this.allocationTracker = allocationTracker; this.bytesPerKey = bytesPerKey; this.bytesPerEntry = 4 + bytesPerKey; this.valueMarker = valueMarker; @@ -54,7 +58,8 @@ protected UnsafeTable( int capacity, int bytesPerKey, VALUE valueMarker ) if ( UnsafeUtil.allowUnalignedMemoryAccess ) { - this.allocatedAddress = this.address = UnsafeUtil.allocateMemory( dataSize ); + bytesToAllocate = dataSize; + this.allocatedAddress = this.address = allocateMemory( bytesToAllocate ); } else { @@ -69,7 +74,8 @@ protected UnsafeTable( int capacity, int bytesPerKey, VALUE valueMarker ) " yielding a bytesPerEntry:" + bytesPerEntry + ", which isn't 4-byte aligned." ); } - this.allocatedAddress = UnsafeUtil.allocateMemory( dataSize + Integer.BYTES - 1 ); + bytesToAllocate = dataSize + Integer.BYTES - 1; + this.allocatedAddress = allocateMemory( bytesToAllocate ); this.address = UnsafeUtil.alignedMemory( allocatedAddress, Integer.BYTES ); } @@ -190,7 +196,7 @@ public void removeHopBit( int index, int hd ) @Override public void close() { - UnsafeUtil.free( allocatedAddress ); + deallocateMemory(); } protected static void alignmentSafePutLongAsTwoInts( long address, long value ) @@ -219,4 +225,17 @@ protected static long alignmentSafeGetLongAsTwoInts( long address ) long msb = UnsafeUtil.getInt( address + Integer.BYTES ) & 0xFFFFFFFFL; return lsb | (msb << Integer.SIZE); } + + private long allocateMemory( long bytesToAllocate ) + { + long address = UnsafeUtil.allocateMemory( bytesToAllocate ); + allocationTracker.allocate( bytesToAllocate ); + return address; + } + + private void deallocateMemory() + { + UnsafeUtil.free( allocatedAddress ); + allocationTracker.deallocate( bytesToAllocate ); + } } diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveIntCollectionsTest.java b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveIntCollectionsTest.java index 7bb3541d784bc..5d29ff960e597 100644 --- a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveIntCollectionsTest.java +++ b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveIntCollectionsTest.java @@ -25,6 +25,8 @@ import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicInteger; +import org.neo4j.memory.GlobalMemoryTracker; + import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertArrayEquals; @@ -54,7 +56,7 @@ public void arrayOfItemsAsIterator() throws Exception public void convertCollectionToLongArray() { PrimitiveIntSet heapSet = PrimitiveIntCollections.asSet( new int[]{1, 2, 3} ); - PrimitiveIntSet offHeapIntSet = Primitive.offHeapIntSet(); + PrimitiveIntSet offHeapIntSet = Primitive.offHeapIntSet( GlobalMemoryTracker.INSTANCE ); offHeapIntSet.add( 7 ); offHeapIntSet.add( 8 ); assertArrayEquals( new long[]{1, 2, 3}, PrimitiveIntCollections.asLongArray( heapSet ) ); diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/BasicTableTest.java b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/BasicTableTest.java index 00dea23b32fa6..b4241f29a25e6 100644 --- a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/BasicTableTest.java +++ b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/BasicTableTest.java @@ -28,12 +28,11 @@ import java.util.Random; import org.neo4j.collection.primitive.Primitive; +import org.neo4j.memory.GlobalMemoryTracker; +import static java.lang.System.currentTimeMillis; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; - -import static java.lang.System.currentTimeMillis; - import static org.neo4j.collection.primitive.Primitive.VALUE_MARKER; @RunWith( Parameterized.class ) @@ -93,7 +92,7 @@ public Object sampleValue() @Override public Table newTable( int capacity ) { - return new IntKeyUnsafeTable( capacity, VALUE_MARKER ); + return new IntKeyUnsafeTable( capacity, VALUE_MARKER, GlobalMemoryTracker.INSTANCE ); } @Override @@ -113,7 +112,7 @@ public Object sampleValue() @Override public Table newTable( int capacity ) { - return new LongKeyUnsafeTable( capacity, VALUE_MARKER ); + return new LongKeyUnsafeTable( capacity, VALUE_MARKER, GlobalMemoryTracker.INSTANCE ); } @Override @@ -193,7 +192,7 @@ public Object sampleValue() @Override public Table newTable( int capacity ) { - return new LongKeyLongValueUnsafeTable( capacity ); + return new LongKeyLongValueUnsafeTable( capacity, GlobalMemoryTracker.INSTANCE ); } @Override diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveCollectionEqualityTest.java b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveCollectionEqualityTest.java index 38ae732cf2552..1b76479b281fc 100644 --- a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveCollectionEqualityTest.java +++ b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveCollectionEqualityTest.java @@ -39,6 +39,7 @@ import org.neo4j.collection.primitive.PrimitiveLongObjectMap; import org.neo4j.collection.primitive.PrimitiveLongSet; import org.neo4j.function.Factory; +import org.neo4j.memory.GlobalMemoryTracker; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -272,10 +273,12 @@ public boolean remove( PrimitiveLongObjectMap coll ) public static Factory intSetWithCapacity = () -> Primitive.intSet( randomCapacity() ); @DataPoint - public static Factory offheapIntSet = Primitive::offHeapIntSet; + public static Factory offheapIntSet = + () -> Primitive.offHeapIntSet( GlobalMemoryTracker.INSTANCE ); @DataPoint - public static Factory offheapIntSetWithCapacity = () -> Primitive.offHeapIntSet( randomCapacity() ); + public static Factory offheapIntSetWithCapacity = + () -> Primitive.offHeapIntSet( randomCapacity(), GlobalMemoryTracker.INSTANCE ); @DataPoint public static Factory longSet = Primitive::longSet; @@ -284,10 +287,12 @@ public boolean remove( PrimitiveLongObjectMap coll ) public static Factory longSetWithCapacity = () -> Primitive.longSet( randomCapacity() ); @DataPoint - public static Factory offheapLongSet = Primitive::offHeapLongSet; + public static Factory offheapLongSet = + () -> Primitive.offHeapLongSet( GlobalMemoryTracker.INSTANCE ); @DataPoint - public static Factory offheapLongSetWithCapacity = () -> Primitive.offHeapLongSet( randomCapacity() ); + public static Factory offheapLongSetWithCapacity = + () -> Primitive.offHeapLongSet( randomCapacity(), GlobalMemoryTracker.INSTANCE ); @DataPoint public static Factory intLongMap = Primitive::intLongMap; @@ -309,11 +314,12 @@ public boolean remove( PrimitiveLongObjectMap coll ) () -> Primitive.longLongMap( randomCapacity() ); @DataPoint - public static Factory offheapLongLongMap = Primitive::offHeapLongLongMap; + public static Factory offheapLongLongMap = + () -> Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ); @DataPoint public static Factory offheapLongLongMapWithCapacity = - () -> Primitive.offHeapLongLongMap( randomCapacity() ); + () -> Primitive.offHeapLongLongMap( randomCapacity(), GlobalMemoryTracker.INSTANCE ); @DataPoint public static Factory intObjMap = Primitive::intObjectMap; diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongMapTest.java b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongMapTest.java index 9454381f71fa0..44e5b98e3746f 100644 --- a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongMapTest.java +++ b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongMapTest.java @@ -42,6 +42,7 @@ import org.neo4j.collection.primitive.PrimitiveLongObjectMap; import org.neo4j.collection.primitive.PrimitiveLongObjectVisitor; import org.neo4j.collection.primitive.PrimitiveLongVisitor; +import org.neo4j.memory.GlobalMemoryTracker; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; @@ -984,7 +985,7 @@ public void longLongOffHeapEntryVisitorShouldSeeAllEntriesIfItDoesNotBreakOut() { // GIVEN PrimitiveLongLongVisitor visitor; - try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap() ) + try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ) ) { map.put( 1, 100 ); map.put( 2, 200 ); @@ -1007,7 +1008,7 @@ public void longLongOffHeapEntryVisitorShouldNotSeeEntriesAfterRequestingBreakOu { // GIVEN AtomicInteger counter = new AtomicInteger(); - try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap() ) + try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ) ) { map.put( 1, 100 ); map.put( 2, 200 ); @@ -1188,7 +1189,7 @@ public void longLongOffHeapKeyVisitorShouldSeeAllEntriesIfItDoesNotBreakOut() { // GIVEN PrimitiveLongVisitor visitor = mock( PrimitiveLongVisitor.class ); - try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap() ) + try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ) ) { map.put( 1, 100 ); map.put( 2, 200 ); @@ -1210,7 +1211,7 @@ public void longLongOffHeapKeyVisitorShouldNotSeeEntriesAfterRequestingBreakOut( { // GIVEN AtomicInteger counter = new AtomicInteger(); - try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap() ) + try ( PrimitiveLongLongMap map = Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ) ) { map.put( 1, 100 ); map.put( 2, 200 ); @@ -1335,7 +1336,6 @@ public void recursivePutGrowInterleavingShouldNotDropOriginalValues() @Test public void recursivePutGrowInterleavingShouldNotDropOriginalValuesEvenWhenFirstGrowAddsMoreValuesAfterSecondGrow() - throws Exception { // List of values that cause recursive growth like above, but this time the first grow wants to add more values // to the table *after* the second grow has occurred. diff --git a/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java b/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java index b052c1d7cee79..5608f8aa5d0bb 100644 --- a/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java +++ b/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java @@ -43,6 +43,7 @@ import org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommit; import org.neo4j.kernel.impl.transaction.log.files.LogFiles; import org.neo4j.kernel.impl.transaction.log.files.LogFilesBuilder; +import org.neo4j.memory.GlobalMemoryTracker; import org.neo4j.storageengine.api.StorageCommand; import org.neo4j.tools.txlog.checktypes.CheckType; import org.neo4j.tools.txlog.checktypes.CheckTypes; @@ -127,7 +128,7 @@ boolean validateCheckPoints( LogFiles logFiles, InconsistenciesHandler handler ) final long lowestLogVersion = logFiles.getLowestLogVersion(); final long highestLogVersion = logFiles.getHighestLogVersion(); boolean success = true; - try ( PrimitiveLongLongMap logFileSizes = Primitive.offHeapLongLongMap() ) + try ( PrimitiveLongLongMap logFileSizes = Primitive.offHeapLongLongMap( GlobalMemoryTracker.INSTANCE ) ) { for ( long i = lowestLogVersion; i <= highestLogVersion; i++ ) {