哪个更快:Java堆还是本地内存

  使用Java的一个好处就是你可以不用亲自来管理内存的分配和释放。当你用 new 关键字来实例化一个对象时,它所需的内存会自动的在Java堆中分配。堆会被垃圾回收器进行管理,并且它会在对象超出作用域时进行内存回收。但是在JVM中有一个‘后门’可以让你访问不在堆中的本地内存(native memory)。在这篇文章中,我会给你演示一个对象是怎样以连续的字节码的方式在内存中进行存储,并且告诉你是应该怎样存储这些字节,是在Java堆中还是在本地内存中。最后我会就怎样从JVM中访问内存更快给一些结论:是用Java堆还是本地内存。

  使用 Unsafe 来分配和回收内存

  sun.misc.Unsafe 可以让你在Java中分配和回收本地内存,就像C语言中的 malloc 和 free 。通过它分配的内存不在Java堆中,并且不受垃圾回收器的管理,因此在它被使用完的时候你需要自己来负责释放和回收。下面是我写的一个使用 Unsafe 来管理本地内存的一个工具类:

  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);

  }

  }

  在本地内存中分配一个对象

  让我们来将下面的Java对象放到本地内存中:

  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;

  }

  }

  我们所做的仅仅是把对象的属性放入到 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);

  }

  }

  现在我们来看看对两个数组的读写性能:其中一个含有数百万的 SomeObject 对象,另外一个含有数百万的 SomeMemoryObject 对象。

  // 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