One benefit of using Java is that you don't have to manage memory allocation and release yourself. When you instantiate an object using the new
keyword, the memory it requires is automatically allocated in the Java heap. The heap is managed by the garbage collector, and it reclaims memory when objects go out of scope. But there is a 'backdoor' in the JVM that allows you to access native memory that is not in the heap. In this article, I will show you how an object is stored in memory as a continuous bytecode, and tell you how these bytes should be stored, whether in the Java heap or in local memory . Finally I will give some conclusions on how to access memory from the JVM faster: using the Java heap or local memory.
Unsafe
to allocate and deallocate memory sun.misc.Unsafe
allows you to allocate and deallocate local memory in Java, just like malloc
and free
in C language. The memory allocated through it is not in the Java heap and is not managed by the garbage collector, so you need to be responsible for releasing and recycling it yourself when it is used up. The following is a tool class I wrote that uses Unsafe
to manage local memory:
public class Direct implements Memory { private static Unsafe unsafe; private static boolean AVAILABLE = false; static { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); unsafe = (Unsafe)field.get(null); AVAILABLE = true; } catch(Exception e) { // NOOP: throw exception later when allocating memory } } public static boolean isAvailable() { return AVAILABLE; } private static Direct INSTANCE = null; public static Memory getInstance() { if (INSTANCE == null) { INSTANCE = new Direct(); } return INSTANCE; } private Direct() { } @Override public long alloc(long size) { if (!AVAILABLE) { throw new IllegalStateException("sun.misc.Unsafe is not accessible!"); } return unsafe.allocateMemory(size); } @Override public void free(long address) { unsafe.freeMemory(address); } @Override public final long getLong(long address) { return unsafe.getLong(address); } @Override public final void putLong(long address, long value) { unsafe.putLong(address, value); } @Override public final int getInt(long address) { return unsafe.getInt(address); } @Override public final void putInt(long address, int value) { unsafe.putInt(address, value); } }
Let us convert the following Java The object is placed in local memory:
public class SomeObject { private long someLong; private int someInt; public long getSomeLong() { return someLong; } public void setSomeLong(long someLong) { this.someLong = someLong; } public int getSomeInt() { return someInt; } public void setSomeInt(int someInt) { this.someInt = someInt; } }
All we have done is to put the properties of the object into Memory
:
public class SomeMemoryObject { private final static int someLong_OFFSET = 0; private final static int someInt_OFFSET = 8; private final static int SIZE = 8 + 4; // one long + one int private long address; private final Memory memory; public SomeMemoryObject(Memory memory) { this.memory = memory; this.address = memory.alloc(SIZE); } @Override public void finalize() { memory.free(address); } public final void setSomeLong(long someLong) { memory.putLong(address + someLong_OFFSET, someLong); } public final long getSomeLong() { return memory.getLong(address + someLong_OFFSET); } public final void setSomeInt(int someInt) { memory.putInt(address + someInt_OFFSET, someInt); } public final int getSomeInt() { return memory.getInt(address + someInt_OFFSET); } }
Now Let's take a look at the read and write performance of two arrays: one containing millions of SomeObject
objects, the other containing millions of SomeMemoryObject
objects .
// with JIT: Number of Objects: 1,000 1,000,000 10,000,000 60,000,000 Heap Avg Write: 107 2.30 2.51 2.58 Native Avg Write: 305 6.65 5.94 5.26 Heap Avg Read: 61 0.31 0.28 0.28 Native Avg Read: 309 3.50 2.96 2.16 // without JIT: (-Xint) Number of Objects: 1,000 1,000,000 10,000,000 60,000,000 Heap Avg Write: 104 107 105 102 Native Avg Write: 292 293 300 297 Heap Avg Read: 59 63 60 58 Native Avg Read: 297 298 302 299
Conclusion: Reading local memory across the JVM barrier will be about 10 times slower than reading memory in the Java heap directly, and writing operations will be about 2 times slower. However, it should be noted that since the local memory space managed by each SomeMemoryObject object is independent, the read and write operations are not continuous. Then let's compare the performance of reading and writing continuous memory space.
This test contains the same test data in the heap and in a large piece of continuous local memory. Then we do multiple read and write operations to see which one is faster. And we will do some random address access to compare the results.
// with JIT and sequential access: Number of Objects: 1,000 1,000,000 1,000,000,000 Heap Avg Write: 12 0.34 0.35 Native Avg Write: 102 0.71 0.69 Heap Avg Read: 12 0.29 0.28 Native Avg Read: 110 0.32 0.32 // without JIT and sequential access: (-Xint) Number of Objects: 1,000 1,000,000 10,000,000 Heap Avg Write: 8 8 8 Native Avg Write: 91 92 94 Heap Avg Read: 10 10 10 Native Avg Read: 91 90 94 // with JIT and random access: Number of Objects: 1,000 1,000,000 1,000,000,000 Heap Avg Write: 61 1.01 1.12 Native Avg Write: 151 0.89 0.90 Heap Avg Read: 59 0.89 0.92 Native Avg Read: 156 0.78 0.84 // without JIT and random access: (-Xint) Number of Objects: 1,000 1,000,000 10,000,000 Heap Avg Write: 55 55 55 Native Avg Write: 141 142 140 Heap Avg Read: 55 55 55 Native Avg Read: 138 140 138
Conclusion:When doing continuous access, Java heap memory is usually faster than local memory. For random address access, heap memory is only slightly slower than local memory, and when targeting large blocks of contiguous data, it is not much slower.
Using local memory in Java has its meaning, for example when you want to operate on large blocks of data (>2G) and don't want to use the garbage collector (GC) when. From a latency perspective, direct access to local memory is no faster than accessing the Java heap. This conclusion actually makes sense, because there is definitely overhead in crossing the JVM barrier. This conclusion also applies to ByteBuffer
using local or heap. The speed improvement of using local ByteBuffer is not to access these memories, but that it can directly operate with the local IO provided by the operating system
The above is the detailed content of Which is faster, Java heap or local memory?. For more information, please follow other related articles on the PHP Chinese website!