├── .gitignore ├── README.md ├── build.xml ├── pom.xml ├── src-module-info └── module-info.java └── src ├── main └── java │ └── com │ └── nanosai │ └── memops │ ├── bytes │ ├── BytesAllocatorAutoDefrag.java │ ├── BytesAllocatorBase.java │ ├── BytesAllocatorManualDefrag.java │ └── IBytesAllocator.java │ └── objects │ ├── Bytes.java │ ├── BytesFactory.java │ ├── IObjectFactory.java │ ├── IObjectPool.java │ └── ObjectPool.java └── test └── java └── com └── nanosai └── memops ├── bytes └── BytesAllocatorAutoDefragTest.java └── objects ├── BytesTest.java └── ObjectPoolTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | target/ 4 | out-build/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mem Ops for Java 2 | 3 | - [Introduction](#introduction) 4 | - [Mem Ops Tutorial](#tutorial) 5 | - [The Basics](#basics) 6 | - [Advantages Over Standard Java Byte Array Instantiation](#advantages) 7 | - [Maven Dependency](#maven-dependency) 8 | - [Version History](#version-history) 9 | 10 | 11 | 12 | 13 | # Introduction 14 | 15 | Mem Ops for Java is a Java memory manager / allocator for use in systems requiring steady state memory consumption 16 | (as little garbage collection as possible). 17 | 18 | The main use case is high performance, high reliability, low latency distributed systems, but Mem Ops can be used for 19 | any use case that requires stable memory consumption. 20 | 21 | Mem Ops is developed by [https://nanosai.com](https://nanosai.com) . 22 | 23 | 24 | 25 | 26 | # Mem Ops Tutorial 27 | This README.md page introduces what Mem Ops is, but if you are looking for a tutorial explaining how to use 28 | Mem Ops in more detail, we have one here: 29 | 30 | [http://tutorials.jenkov.com/mem-ops/index.html](http://tutorials.jenkov.com/mem-ops/index.html) 31 | 32 | 33 | 34 | 35 | # The Basics 36 | 37 | Mem Ops has two core packages: 38 | 39 | - com.nanosai.memops.bytes 40 | - com.nanosai.memops.objects 41 | 42 | The com.nanosai.memops.bytes package contains classes for allocating bytes (sections of a bigger byte array). Using these 43 | classes you can allocate a single, big byte array from which you can allocate smaller blocks. That way you only 44 | ever allocate the bigger byte array, even though you subdivide it and use the smaller blocks individually. 45 | When you are done with a byte block, you must explicitly free it again. When 46 | a block is freed it can be reallocated for use again. 47 | 48 | The com.nanosai.memops.objects package contains classes for allocating (pooling) objects. These can be any object 49 | you need. The package comes with some built-in classes for Bytes - which represents a block of bytes in a shared 50 | byte array. Thus, a Bytes instance contains a byte[] array reference, a start index, end index etc. which is used 51 | to identify the byte block in the big byte array. 52 | 53 | 54 | 55 | 56 | # Advantages Over Standard Java Byte Array Instantiation 57 | Allocating byte blocks from a bigger, shared byte array can, in some cases, provide several advantages 58 | over allocating byte arrays via the Java "new" command. I will explain these advantages in the following sections: 59 | 60 | 61 | ## Avoiding Heap Fragmentation 62 | By obtaining a part of a bigger byte array rather than creating a new byte array via the Java language "new" command, 63 | you avoid fragmenting the heap of the Java VM. If the Java VM heap becomes fragmented, the garbage collector will 64 | need to spend considerable time to defragment the heap, so it can be reallocated anew. 65 | 66 | Garbage collection may cause a garbage collection pause. If your application allocates byte arrays at a fast pace, 67 | that may put pressure on the garbage collector and result in uneven performance of your application. 68 | 69 | 70 | ## Defragmentation at Your Convenience 71 | When memory blocks are explicitly freed, the byte allocators can either defragment 72 | the internal byte array immediately, or at a later time when it is convenient for you. This way you get to choose 73 | when defragmentation (garbage collection) takes place. You can either garbage collect immediately when the 74 | memory block is freed, thus paying the price of defragmentation immediately (and just for that memory block), 75 | or you can defragment the big byte array when your application has no or little other work to do. 76 | 77 | Since you cannot explicitly free objects in Java, nor trigger the garbage collector, you do not have these options 78 | with normally allocated byte arrays. 79 | 80 | 81 | ## Checking if Enough Memory is Free Before Allocation 82 | You can check if it possible to allocate the desired memory before actually doing so. If not, you could e.g. 83 | reject an incoming request, or simply not read any more data from inbound sockets etc., in order to provide 84 | backpressure back up your request chain. This is not possible with the Java "new" command either. 85 | 86 | 87 | ##Freeing Unused Bytes of a Block 88 | 89 | 90 | 91 | 92 | 93 | # Maven Dependency 94 | 95 | If you want to use Mem Ops with Maven, the Maven dependency for Mem Ops looks like this: 96 | 97 | 98 | com.nanosai 99 | mem-ops 100 | 0.7.1 101 | 102 | 103 | Remember to substitute the version with the version of Mem Ops you want to use. See the Mem Ops version history in 104 | the next section. 105 | 106 | 107 | 108 | 109 | # Version History 110 | 111 | | Version | Java Version | Change | 112 | |---------|--------------|--------| 113 | | 0.7.1 | Java 8 | Ant script added that can build a Java JPMS module for Mem Ops. No functional changes otherwise. | 114 | | 0.6.4 | Java 8 | Fixed a small bug in Bytes.allocate(), so all indexes are now set / reset correctly after allocation. | 115 | | 0.6.2 | Java 8 | Updated Bytes + BytesFactory to use IBytesAllocator instead of ByteAllocatorDefrag. Using IBytesAllocator allows for use of BytesAllocatorManualDefrag too. | 116 | | 0.6.0 | Java 8 | IObjectFactory interface instance() method had ObjectPool added as parameter. | 117 | | 0.5.1 | Java 8 | First release | 118 | 119 | 120 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | 7 | com.nanosai 8 | mem-ops 9 | 0.7.1 10 | jar 11 | 12 | Mem Ops for Java 13 | 14 | Mem Ops for Java is an open source toolkit for memory management for use in applications that require 15 | steady state memory consumption, meaning a constant amount of memory used, and as little garbage collection 16 | as possible. 17 | 18 | 19 | https://github.com/nanosai/mem-ops-java 20 | 21 | 22 | 23 | The Apache License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | 26 | 27 | 28 | 29 | 30 | Jakob Jenkov 31 | jakob@jenkov.com 32 | Jenkov Aps (on behalf of Nanosai.com) 33 | http://www.nanosai.com 34 | 35 | 36 | 37 | 38 | scm:git:git://github.com/nanosai/mem-ops-java.git 39 | scm:git:ssh://github.com:nanosai/mem-ops-java.git 40 | http://github.com/nanosai/mem-ops-java/tree/master 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.junit.jupiter 48 | junit-jupiter 49 | 5.4.2 50 | test 51 | 52 | 53 | 54 | 55 | 56 | 57 | ossrh 58 | https://oss.sonatype.org/content/repositories/snapshots 59 | 60 | 61 | ossrh 62 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-compiler-plugin 72 | 3.8.0 73 | 74 | 11 75 | 76 | 77 | 78 | 79 | 80 | org.sonatype.plugins 81 | nexus-staging-maven-plugin 82 | 1.6.8 83 | true 84 | 85 | ossrh 86 | https://oss.sonatype.org/ 87 | true 88 | 89 | 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-source-plugin 96 | 2.4 97 | 98 | 99 | attach-sources 100 | 101 | jar-no-fork 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-javadoc-plugin 110 | 2.10.1 111 | 112 | 113 | attach-javadocs 114 | 115 | jar 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-gpg-plugin 124 | 1.6 125 | 126 | 127 | sign-artifacts 128 | verify 129 | 130 | sign 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | UTF-8 141 | 142 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /src-module-info/module-info.java: -------------------------------------------------------------------------------- 1 | module com.nanosai.memops { 2 | exports com.nanosai.memops.bytes; 3 | exports com.nanosai.memops.objects; 4 | 5 | } -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/bytes/BytesAllocatorAutoDefrag.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.bytes; 2 | 3 | /** 4 | * The BytesAllocatorAutoDefrag class is capable of allocating (and freeing) smaller sections of a larger byte array. 5 | * The underlying larger byte array is passed to the BytesAllocatorAutoDefrag when it is instantiated. 6 | * 7 | * When a block (section) of the bigger array is allocated, it is allocated from the first free block that 8 | * has the same or larger size as the block requested. In other words, if you request a block of 1024 bytes, 9 | * those bytes will be allocated from the first free section that is 1024 bytes or larger. 10 | * 11 | * */ 12 | public class BytesAllocatorAutoDefrag extends BytesAllocatorBase implements IBytesAllocator { 13 | 14 | public BytesAllocatorAutoDefrag(byte[] data) { 15 | init(data); 16 | } 17 | 18 | 19 | public void free(int from, int to) { 20 | long freeBlockDescriptor = from; 21 | freeBlockDescriptor <<=32; 22 | freeBlockDescriptor += ((long) to); 23 | 24 | for(int i=0; i 0 && (from == (int) (this.freeBlocks[i-1] & TO_AND_MASK)); 28 | boolean mergeWithNextBlock = (to == (int) (this.freeBlocks[i] >> 32)); 29 | 30 | if(mergeWithPreviousBlock && mergeWithNextBlock) { 31 | this.freeBlocks[i-1] = (this.freeBlocks[i-1] & FROM_AND_MASK) | (this.freeBlocks[i] & TO_AND_MASK); 32 | int length = this.freeBlockCount - i -1; 33 | System.arraycopy(this.freeBlocks, i+1, this.freeBlocks, i, this.freeBlockCount - i -1); 34 | this.freeBlockCount--; 35 | return; 36 | } 37 | if(mergeWithPreviousBlock){ 38 | this.freeBlocks[i-1] = (this.freeBlocks[i-1] & FROM_AND_MASK) | to; 39 | return; 40 | } 41 | if(mergeWithNextBlock){ 42 | this.freeBlocks[i] = (from << 32) | (this.freeBlocks[i] & TO_AND_MASK); 43 | return; 44 | } 45 | 46 | //new free block is not adjacent to either previous nor next block, so insert it here. 47 | //todo check if this can result in a freeBlocks IndexOutOfBoundsException - if there are more free blocks than freeBlocks has space for. 48 | int length = this.freeBlockCount - i; 49 | System.arraycopy(this.freeBlocks, i, this.freeBlocks, i+1, length); 50 | this.freeBlocks[i] = freeBlockDescriptor; 51 | this.freeBlockCount++; 52 | return; 53 | } 54 | } 55 | 56 | //no place found to insert the free block, so append it at the end instead. 57 | //todo maybe extract if-statement into an "expandFreeBlocksArrayIfNecessary() method? 58 | if(this.freeBlockCount >= this.freeBlocks.length) { 59 | //expand array 60 | long[] newFreeBlocks = new long[this.freeBlocks.length + FREE_BLOCK_ARRAY_SIZE_INCREMENT]; 61 | System.arraycopy(this.freeBlocks, 0, newFreeBlocks, 0, this.freeBlocks.length); 62 | this.freeBlocks = newFreeBlocks; 63 | } 64 | this.freeBlocks[freeBlockCount] = freeBlockDescriptor; 65 | freeBlockCount++; 66 | } 67 | 68 | public void defragment() { 69 | //NOP - defragment happens already when blocks are freed. 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/bytes/BytesAllocatorBase.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.bytes; 2 | 3 | /** 4 | * A common base class for the two concrete IBytesAllcator implementations BytesAllocatorAutoDefag and 5 | * BytesAllocatorManualDefrag. The allocation of bytes is done in the exact same way, hence that is 6 | * implemented in this base class. Only the freeing of blocks is different in the two subclasses. 7 | */ 8 | public abstract class BytesAllocatorBase implements IBytesAllocator { 9 | 10 | protected static int FREE_BLOCK_ARRAY_SIZE_INCREMENT = 16; 11 | protected static long FROM_AND_MASK = (long) 0xFFFFFFFF00000000L; 12 | protected static long TO_AND_MASK = (long) 0x00000000FFFFFFFFL; 13 | 14 | 15 | protected byte[] data = null; 16 | protected long[] freeBlocks = new long[FREE_BLOCK_ARRAY_SIZE_INCREMENT]; 17 | 18 | protected int freeBlockCount = 0; 19 | 20 | 21 | 22 | protected void init(byte[] data) { 23 | this.data = data; 24 | free(0, data.length); 25 | } 26 | 27 | public byte[] getData() { 28 | return this.data; 29 | } 30 | 31 | public int capacity() { 32 | return this.data.length; 33 | } 34 | 35 | public int freeBlockCount() { 36 | return this.freeBlockCount; 37 | } 38 | 39 | public int freeBlockStartIndex(int freeBlockIndex) { 40 | long freeBlockDescriptor = this.freeBlocks[freeBlockIndex]; 41 | long startIndex = freeBlockDescriptor & FROM_AND_MASK; 42 | startIndex >>= 32; 43 | 44 | return (int) startIndex; 45 | } 46 | 47 | public int freeBlockEndIndex(int freeBlockIndex) { 48 | long freeBlockDescriptor = this.freeBlocks[freeBlockIndex]; 49 | long startIndex = freeBlockDescriptor & TO_AND_MASK; 50 | return (int) startIndex; 51 | } 52 | 53 | public int freeCapacity() { 54 | int freeCapacity = 0; 55 | for(int i=0; i>=32; 58 | 59 | long to = this.freeBlocks[i]; 60 | to &= TO_AND_MASK; 61 | 62 | freeCapacity += (to - from); 63 | } 64 | 65 | return freeCapacity; 66 | } 67 | 68 | /** 69 | * This method allocates a section of the internal byte array from the first free block of that array found. 70 | * You should not call this method directly. Rather, obtain a MemoryBlock via getMemoryBlock() and call 71 | * MemoryBlock.allocate(size) on that instance. 72 | * 73 | * If a block could be allocated of the requested size, the startIndex into the underlying byte array of 74 | * that block is returned by this method. If no free block was large enough to allocate the requested 75 | * block, -1 is returned. 76 | * 77 | * @param blockSize The requested number of bytes to allocate 78 | * @return The startIndex into the underlying byte array of the allocated block (of the requested size), 79 | * or -1 if the block could not be allocated. 80 | */ 81 | public int allocate(int blockSize){ 82 | 83 | boolean freeBlockFound = false; 84 | 85 | int freeBlockIndex = 0; 86 | 87 | while(!freeBlockFound && freeBlockIndex < this.freeBlockCount){ 88 | long freeBlockFromIndex = this.freeBlocks[freeBlockIndex]; 89 | freeBlockFromIndex >>=32; 90 | 91 | long freeBlockToIndex = this.freeBlocks[freeBlockIndex]; 92 | freeBlockToIndex &= TO_AND_MASK; 93 | 94 | if(blockSize <= (freeBlockToIndex-freeBlockFromIndex)){ 95 | freeBlockFound = true; 96 | 97 | long newBlockDescriptor = freeBlockFromIndex + blockSize; 98 | newBlockDescriptor <<= 32; 99 | 100 | newBlockDescriptor += freeBlockToIndex; 101 | 102 | this.freeBlocks[freeBlockIndex] = newBlockDescriptor; 103 | return (int) freeBlockFromIndex; 104 | } else { 105 | freeBlockIndex++; 106 | } 107 | } 108 | return -1; 109 | } 110 | 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/bytes/BytesAllocatorManualDefrag.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.bytes; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * The BytesAllocatorManualDefrag class is capable of allocating (and freeing) smaller sections of a larger byte array. 7 | * The underlying larger byte array is passed to the BytesAllocatorManualDefrag when it is instantiated. 8 | * 9 | * When a block (section) of the bigger array is allocated, it is allocated from the first free block that 10 | * has the same or larger size as the block requested. In other words, if you request a block of 1024 bytes, 11 | * those bytes will be allocated from the first free section that is 1024 bytes or larger. 12 | * 13 | */ 14 | public class BytesAllocatorManualDefrag extends BytesAllocatorBase implements IBytesAllocator { 15 | 16 | public BytesAllocatorManualDefrag(byte[] data) { 17 | init(data); 18 | } 19 | 20 | 21 | public void free(int from, int to){ 22 | appendFreeBlock(from, to); 23 | } 24 | 25 | protected void appendFreeBlock(long from, long to) { 26 | long freeBlockDescriptor = from; 27 | freeBlockDescriptor <<= 32; 28 | 29 | freeBlockDescriptor += ((long) to); 30 | 31 | if(this.freeBlockCount >= this.freeBlocks.length) { 32 | //expand array 33 | long[] newFreeBlocks = new long[this.freeBlocks.length + FREE_BLOCK_ARRAY_SIZE_INCREMENT]; 34 | System.arraycopy(this.freeBlocks, 0, newFreeBlocks, 0, this.freeBlocks.length); 35 | this.freeBlocks = newFreeBlocks; 36 | } 37 | 38 | 39 | this.freeBlocks[freeBlockCount] = freeBlockDescriptor; 40 | freeBlockCount++; 41 | } 42 | 43 | 44 | public void defragment() { 45 | //sort 46 | Arrays.sort(this.freeBlocks, 0, this.freeBlockCount); 47 | 48 | //merge 49 | int newIndex = 0; 50 | 51 | for(int i=0; i < freeBlockCount;){ 52 | long from = this.freeBlocks[i]; 53 | from >>=32; 54 | 55 | long to = this.freeBlocks[i]; 56 | to &= TO_AND_MASK; 57 | 58 | int nextIndex = i + 1; 59 | 60 | long nextFrom = this.freeBlocks[nextIndex]; 61 | nextFrom >>=32; 62 | 63 | long nextTo = this.freeBlocks[nextIndex]; 64 | nextTo &= TO_AND_MASK; 65 | 66 | while(to == nextFrom ){ 67 | to = nextTo; //todo this can be moved to after while loop? 68 | nextIndex++; 69 | if(nextIndex == this.freeBlockCount){ 70 | break; 71 | } 72 | 73 | nextFrom = this.freeBlocks[nextIndex]; 74 | nextFrom >>=32; 75 | 76 | nextTo = this.freeBlocks[nextIndex]; 77 | nextTo &= TO_AND_MASK; 78 | } 79 | 80 | i = nextIndex; 81 | 82 | long newBlockDescriptor = from; 83 | newBlockDescriptor <<= 32; 84 | 85 | newBlockDescriptor += to; 86 | 87 | this.freeBlocks[newIndex] = newBlockDescriptor; 88 | newIndex++; 89 | } 90 | this.freeBlockCount = newIndex; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/bytes/IBytesAllocator.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.bytes; 2 | 3 | public interface IBytesAllocator { 4 | 5 | public byte[] getData(); 6 | 7 | public int capacity(); 8 | 9 | public int freeBlockCount(); 10 | 11 | public int freeCapacity(); 12 | 13 | public int allocate(int length); 14 | 15 | public void free(int from, int too); 16 | 17 | public void defragment(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/objects/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | import com.nanosai.memops.bytes.IBytesAllocator; 4 | 5 | /** 6 | * A smaller block of a bigger byte array, starting from startIndex and extending length bytes from that. 7 | */ 8 | public class Bytes { 9 | public byte[] data = null; 10 | public int startIndex = 0; 11 | public int length = 0; 12 | public int endIndex = 0; 13 | 14 | public int readIndex = 0; 15 | public int writeIndex = 0; //equal to the length of the block already written to. 16 | 17 | private boolean isComplete = false; 18 | 19 | 20 | public IBytesAllocator byteArrayAllocator = null; 21 | public ObjectPool objectPool = null; 22 | 23 | public void init(IBytesAllocator byteArrayAllocator, ObjectPool objectPool){ 24 | this.byteArrayAllocator = byteArrayAllocator; 25 | this.data = byteArrayAllocator.getData(); 26 | this.objectPool = objectPool; 27 | } 28 | 29 | public int lengthWritten() { 30 | return this.writeIndex - this.startIndex; 31 | } 32 | 33 | 34 | public boolean allocate(int length) { 35 | int startIndex = this.byteArrayAllocator.allocate(length); 36 | if(startIndex == -1) { 37 | return false; 38 | } 39 | 40 | this.startIndex = startIndex; 41 | this.length = length; 42 | this.endIndex = startIndex + length; 43 | this.writeIndex = startIndex; 44 | this.readIndex = startIndex; 45 | return true; 46 | } 47 | 48 | public void free() { 49 | this.byteArrayAllocator.free(this.startIndex, this.startIndex + this.length); 50 | this.objectPool.free(this); 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/objects/BytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | import com.nanosai.memops.bytes.IBytesAllocator; 4 | 5 | public class BytesFactory implements IObjectFactory { 6 | 7 | private IBytesAllocator bytesAllocatorAutoDefrag = null; 8 | 9 | public BytesFactory(IBytesAllocator allocator){ 10 | this.bytesAllocatorAutoDefrag = allocator; 11 | } 12 | 13 | @Override 14 | public Bytes instance(ObjectPool objectPool) { 15 | Bytes block = new Bytes(); 16 | block.init(this.bytesAllocatorAutoDefrag, objectPool); 17 | return block; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/objects/IObjectFactory.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | public interface IObjectFactory { 4 | public T instance(ObjectPool objectPool); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/objects/IObjectPool.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | public interface IObjectPool { 4 | 5 | public int capacity(); 6 | 7 | public T instance(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/nanosai/memops/objects/ObjectPool.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | import java.util.Stack; 4 | 5 | public class ObjectPool implements IObjectPool { 6 | 7 | private int instances = 0; 8 | private int capacity = 0; 9 | private IObjectFactory objectFactory = null; 10 | private Stack stack = new Stack(); 11 | 12 | public ObjectPool(int capacity) { 13 | this.capacity = capacity; 14 | } 15 | 16 | public ObjectPool(int capacity, IObjectFactory objectFactory){ 17 | this.capacity = capacity; 18 | this.objectFactory = objectFactory; 19 | } 20 | 21 | public int instances() { 22 | return this.instances; 23 | } 24 | 25 | public int instancesFree() { 26 | return this.stack.size(); 27 | } 28 | 29 | @Override 30 | public int capacity() { 31 | return this.capacity; 32 | } 33 | 34 | public void setObjectFactory(IObjectFactory objectFactory){ 35 | this.objectFactory = objectFactory; 36 | } 37 | 38 | @Override 39 | public T instance() { 40 | if(this.stack.size() > 0) { 41 | return this.stack.pop(); 42 | } 43 | if(this.instances == this.capacity) { 44 | return null; 45 | } 46 | this.instances++; 47 | return this.objectFactory.instance(this); 48 | } 49 | 50 | public void free(T instance){ 51 | this.stack.push(instance); 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/nanosai/memops/bytes/BytesAllocatorAutoDefragTest.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.bytes; 2 | 3 | import com.nanosai.memops.bytes.BytesAllocatorAutoDefrag; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | public class BytesAllocatorAutoDefragTest { 9 | 10 | 11 | @Test 12 | public void test() { 13 | BytesAllocatorAutoDefrag allocator = new BytesAllocatorAutoDefrag(new byte[1024]); 14 | 15 | assertEquals( 16, allocator.freeBlocks.length); 16 | assertEquals( 1, allocator.freeBlockCount); 17 | assertEquals( 0, allocator.freeBlockStartIndex(0)); 18 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 19 | 20 | 21 | int offset1 = allocator.allocate(16); 22 | assertEquals (1, allocator.freeBlockCount); 23 | assertEquals( 16, allocator.freeBlockStartIndex(0)); 24 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 25 | 26 | int offset2 = allocator.allocate(16); 27 | assertEquals (1, allocator.freeBlockCount); 28 | assertEquals( 32, allocator.freeBlockStartIndex(0)); 29 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 30 | 31 | int offset3 = allocator.allocate(16); 32 | assertEquals (1, allocator.freeBlockCount); 33 | assertEquals( 48, allocator.freeBlockStartIndex(0)); 34 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 35 | 36 | int offset4 = allocator.allocate(16); 37 | assertEquals (1, allocator.freeBlockCount); 38 | assertEquals( 64, allocator.freeBlockStartIndex(0)); 39 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 40 | 41 | int offset5 = allocator.allocate(16); 42 | assertEquals (1, allocator.freeBlockCount); 43 | assertEquals( 80, allocator.freeBlockStartIndex(0)); 44 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 45 | 46 | allocator.free(offset2,offset2 + 16); 47 | assertEquals (2, allocator.freeBlockCount); 48 | assertEquals( 16, allocator.freeBlockStartIndex(0)); 49 | assertEquals( 32, allocator.freeBlockEndIndex(0)); 50 | assertEquals( 80, allocator.freeBlockStartIndex(1)); 51 | assertEquals(1024, allocator.freeBlockEndIndex(1)); 52 | 53 | 54 | allocator.free(offset4,offset4 + 16); 55 | assertEquals (3, allocator.freeBlockCount); 56 | assertEquals( 16, allocator.freeBlockStartIndex(0)); 57 | assertEquals( 32, allocator.freeBlockEndIndex(0)); 58 | assertEquals( 48, allocator.freeBlockStartIndex(1)); 59 | assertEquals( 64, allocator.freeBlockEndIndex(1)); 60 | assertEquals( 80, allocator.freeBlockStartIndex(2)); 61 | assertEquals(1024, allocator.freeBlockEndIndex(2)); 62 | 63 | allocator.free(offset3,offset3 + 16); 64 | assertEquals (2, allocator.freeBlockCount); 65 | assertEquals( 16, allocator.freeBlockStartIndex(0)); 66 | assertEquals( 64, allocator.freeBlockEndIndex(0)); 67 | assertEquals( 80, allocator.freeBlockStartIndex(1)); 68 | assertEquals(1024, allocator.freeBlockEndIndex(1)); 69 | 70 | allocator.free(offset5,offset5 + 16); 71 | assertEquals (1, allocator.freeBlockCount); 72 | assertEquals( 16, allocator.freeBlockStartIndex(0)); 73 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 74 | 75 | allocator.free(offset1,offset1 + 16); 76 | assertEquals (1, allocator.freeBlockCount); 77 | assertEquals( 0, allocator.freeBlockStartIndex(0)); 78 | assertEquals(1024, allocator.freeBlockEndIndex(0)); 79 | 80 | 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/com/nanosai/memops/objects/BytesTest.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | import com.nanosai.memops.bytes.BytesAllocatorAutoDefrag; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | 9 | public class BytesTest { 10 | 11 | @Test 12 | public void testInstantiationViaObjectPool() { 13 | 14 | BytesAllocatorAutoDefrag byteArrayAllocator = new BytesAllocatorAutoDefrag(new byte[1024 * 1024]); 15 | 16 | BytesFactory bytesFactory = new BytesFactory(byteArrayAllocator); 17 | 18 | ObjectPool objectPool = new ObjectPool(3, bytesFactory); 19 | 20 | Bytes bytes1 = objectPool.instance(); 21 | Bytes bytes2 = objectPool.instance(); 22 | Bytes bytes3 = objectPool.instance(); 23 | Bytes bytes4 = objectPool.instance(); 24 | 25 | assertNotSame(bytes1, bytes2); 26 | assertNotSame(bytes1, bytes3); 27 | assertNotSame(bytes2, bytes3); 28 | 29 | assertNull(bytes4); 30 | } 31 | 32 | @Test 33 | public void testBytesAllocation() { 34 | BytesAllocatorAutoDefrag byteArrayAllocator = new BytesAllocatorAutoDefrag(new byte[1024 * 1024]); 35 | 36 | BytesFactory bytesFactory = new BytesFactory(byteArrayAllocator); 37 | 38 | ObjectPool objectPool = new ObjectPool(3,bytesFactory); 39 | 40 | assertEquals(0, objectPool.instancesFree()); 41 | assertEquals(0, objectPool.instances()); 42 | 43 | Bytes bytes1 = objectPool.instance(); 44 | assertEquals(0, objectPool.instancesFree()); 45 | 46 | assertEquals(1024 * 1024, byteArrayAllocator.freeCapacity()); 47 | bytes1.allocate(1024); 48 | assertEquals((1024 * 1024) - 1024, byteArrayAllocator.freeCapacity()); 49 | 50 | bytes1.free(); 51 | assertEquals(1024 * 1024, byteArrayAllocator.freeCapacity()); 52 | assertEquals(1, objectPool.instancesFree()); 53 | assertEquals(1, objectPool.instances()); 54 | 55 | 56 | int instances = objectPool.instances(); 57 | System.out.println(instances); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/nanosai/memops/objects/ObjectPoolTest.java: -------------------------------------------------------------------------------- 1 | package com.nanosai.memops.objects; 2 | 3 | import com.nanosai.memops.bytes.BytesAllocatorAutoDefrag; 4 | import com.nanosai.memops.objects.Bytes; 5 | import com.nanosai.memops.objects.BytesFactory; 6 | import com.nanosai.memops.objects.IObjectFactory; 7 | import com.nanosai.memops.objects.ObjectPool; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | public class ObjectPoolTest { 13 | 14 | @Test 15 | public void testInstance() { 16 | IObjectFactory stringFactory = new IObjectFactory() { 17 | int instanceNo = 0; 18 | @Override 19 | public String instance(ObjectPool objectPool) { 20 | String value = "" + instanceNo; 21 | instanceNo++; 22 | return value; 23 | } 24 | }; 25 | 26 | IObjectFactory stringFactory2 = (objectPool) -> "" + System.currentTimeMillis(); 27 | 28 | ObjectPool objectPool = new ObjectPool(8, stringFactory); 29 | assertEquals(8, objectPool.capacity()); 30 | 31 | String instance0 = objectPool.instance(); 32 | assertEquals("0", instance0); 33 | assertEquals(1, objectPool.instances()); 34 | 35 | String instance1 = objectPool.instance(); 36 | assertEquals("1", instance1); 37 | assertEquals(2, objectPool.instances()); 38 | 39 | objectPool.free(instance0); //instance0 put on internal stack 40 | objectPool.free(instance1); //instance1 put on internal stack 41 | 42 | String instance1_2 = objectPool.instance(); //get instance1 again 43 | String instance0_2 = objectPool.instance(); //get instance0 again 44 | 45 | assertEquals("1", instance1_2); 46 | assertEquals("0", instance0_2); 47 | 48 | assertSame(instance0, instance0_2); 49 | assertSame(instance1, instance1_2); 50 | 51 | assertNotNull(objectPool.instance()); 52 | assertNotNull(objectPool.instance()); 53 | assertNotNull(objectPool.instance()); 54 | assertNotNull(objectPool.instance()); 55 | assertNotNull(objectPool.instance()); 56 | assertNotNull(objectPool.instance()); 57 | 58 | String instance9 = objectPool.instance(); 59 | assertNull(instance9); //null, because object pool only has a capacity of 8 instances. 60 | } 61 | 62 | @Test 63 | public void testByteArrayBlock() { 64 | BytesAllocatorAutoDefrag allocator = new BytesAllocatorAutoDefrag(new byte[1024]); 65 | ObjectPool objectPool = new ObjectPool<>(8, new BytesFactory(allocator) ); 66 | 67 | Bytes bytes1 = objectPool.instance(); 68 | Bytes bytes2 = objectPool.instance(); 69 | 70 | assertNotSame(bytes1, bytes2); 71 | 72 | assertEquals(0, bytes1.startIndex); 73 | assertEquals(0, bytes1.length); 74 | 75 | assertTrue(bytes1.allocate(16)); 76 | assertEquals(0, bytes1.startIndex); 77 | assertEquals(16, bytes1.length); 78 | 79 | assertTrue(bytes2.allocate(16)); 80 | assertEquals(16, bytes2.startIndex); 81 | assertEquals(16, bytes2.length); 82 | 83 | } 84 | } 85 | --------------------------------------------------------------------------------