From 5fb9e785358e740f1b3aa7b292b2ae593e4f94d6 Mon Sep 17 00:00:00 2001 From: MishaDemianenko Date: Tue, 30 Jan 2018 13:58:50 +0100 Subject: [PATCH] Native memory allocation trackers Introduce possibility to track native memory that we allocate. allow track allocations in global and local contexts, where local contexts can be context of any granularity. Update primitive off-heap collections to report memory allocation and de-allocations. --- .../org/neo4j/memory/GlobalMemoryTracker.java | 57 ++++++++++++++++ .../org/neo4j/memory/LocalMemoryTracker.java | 54 +++++++++++++++ .../neo4j/memory/MemoryAllocationTracker.java | 38 +++++++++++ .../java/org/neo4j/memory/MemoryTracker.java | 32 +++++++++ .../neo4j/memory/GlobalMemoryTrackerTest.java | 52 ++++++++++++++ .../neo4j/memory/LocalMemoryTrackerTest.java | 68 +++++++++++++++++++ .../kernel/impl/locking/ResourceTypesIT.java | 3 +- .../java/org/neo4j/test/FakeCpuClock.java | 7 +- .../org/neo4j/test/FakeHeapAllocation.java | 3 +- .../neo4j/collection/primitive/Primitive.java | 25 +++---- .../hopscotch/IntKeyUnsafeTable.java | 7 +- .../LongKeyLongValueUnsafeTable.java | 8 ++- .../hopscotch/LongKeyUnsafeTable.java | 8 ++- .../primitive/hopscotch/UnsafeTable.java | 27 ++++++-- .../PrimitiveIntCollectionsTest.java | 4 +- .../primitive/hopscotch/BasicTableTest.java | 11 ++- .../PrimitiveCollectionEqualityTest.java | 18 +++-- .../hopscotch/PrimitiveLongMapTest.java | 10 +-- .../org/neo4j/tools/txlog/CheckTxLogs.java | 3 +- 19 files changed, 386 insertions(+), 49 deletions(-) create mode 100644 community/common/src/main/java/org/neo4j/memory/GlobalMemoryTracker.java create mode 100644 community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java create mode 100644 community/common/src/main/java/org/neo4j/memory/MemoryAllocationTracker.java create mode 100644 community/common/src/main/java/org/neo4j/memory/MemoryTracker.java create mode 100644 community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java create mode 100644 community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java 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++ ) {