├── .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 |
--------------------------------------------------------------------------------