使用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

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

结论:跨越JVM的屏障来读本地内存大约会比直接读Java堆中的内存慢10倍,而对于写操作会慢大约2倍。但是需要注意的是,由于每一个SomeMemoryObject对象所管理的本地内存空间都是独立的,因此读写操作都不是连续的。那么我们接下来就来对比下读写连续的内存空间的性能。

访问一大块的连续内存空间

这个测试分别在堆中和一大块连续本地内存中包含了相同的测试数据。然后我们来做多次的读写操作看看哪个更快。并且我们会做一些随机地址的访问来对比结果。

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

结论:在做连续访问的时候,Java堆内存通常都比本地内存要快。对于随机地址访问,堆内存仅仅比本地内存慢一点点,并且是针对大块连续数据的时候,而且没有慢很多。

最后的结论

在Java中使用本地内存有它的意义,比如当你要操作大块的数据时(>2G)并且不想使用垃圾回收器(GC)的时候。从延迟的角度来说,直接访问本地内存不会比访问Java堆快。这个结论其实是有道理的,因为跨越JVM屏障肯定是有开销的。这样的结论对使用本地还是堆的ByteBuffer同样适用。使用本地ByteBuffer的速度提升不在于访问这些内存,而是它可以直接与操作系统提供的本地IO进行操作。

java 本地内存_哪个更快:Java堆还是本地内存相关推荐

  1. java和php哪个运行更快,java和php哪个入门快?-php教程

    跟着互联网的高速倒退,愈来愈多的人开端抉择处置较量争论机行业,而想要处置相干工作的话,理解相干编程言语也是必备的一项技艺.可是有不少冤家正在抉择要学习的编程言语时就被难到了,想晓得哪一种言语入门更快, ...

  2. 新手Java程序员找工作更看重Java项目经验?

    动力节点IT培训,全真项目实操实训,贯穿八大行业,彻底帮助学员摆脱纸上谈兵的尴尬,一技成,天下行. 新手Java程序员找工作更看重Java项目经验?这个问题,想必是很多学Java的朋友也特别好奇的地方 ...

  3. stack vs heap:栈区分配内存快还是堆区分配内存快 ?

    作者 | 码农的荒岛求生 来源 | 码农的荒岛求生 有伙伴问到底是从栈上分配内存快还是从堆上分配内存快,这是个比较基础的问题,今天就来聊一聊. 栈区的内存申请与释放 毫无疑问,显然从栈上分配内存更快, ...

  4. nginx内存池大小快内存_使用直接内存时可以更快

    nginx内存池大小快内存 总览 使用直接内存不能保证提高性能. 考虑到它增加了复杂性,除非有充分的理由使用它,否则应避免使用它. 塞尔吉奥·奥利维拉(Sergio Oliveira Jr)的这篇出色 ...

  5. java高频面试_C ++或Java,高频交易哪个更快?

    java高频面试 总览 关于什么是高频交易的最佳解决方案,存在不同意见. 问题的一部分是高频交易的变化超出您的预期,另一部分是更快的含义. 我的看法 如果您有一个典型的Java程序员和一个典型的C + ...

  6. C ++或Java,高频交易哪个更快?

    总览 关于什么是高频交易的最佳解决方案,存在不同意见. 问题的一部分是高频交易的变化超出您的预期,另一部分是更快的含义. 我的看法 如果您有一个典型的Java程序员和一个典型的C ++程序员,并且每个 ...

  7. 转载:JAVA获取时间戳,哪个更快

    转载:http://hi.baidu.com/suofang/item/96629a060a44c119eafe38cc 目前获取毫秒值大概有下面三种方法 Java代码   //方法 一    Sys ...

  8. JAVA获取时间戳,哪个更快

    目前获取毫秒值大概有下面三种方法 //方法 一 System.currentTimeMillis(); //方法 二 Calendar.getInstance().getTimeInMillis(); ...

  9. python比java容易学_是不是Python比Java更容易学,更容易就业?

    首先,从编程语言本身的语法结构,Java语言更为困难.对初学者来说,学习Java语言的时间成本较高,并且由于Java语言本身有一个相对高度的抽象,你必须想很多实验需要形成自己的Java编程思想完成.J ...

最新文章

  1. sqlite3常用命令以及django如何操作sqlite3数据库
  2. 演技之神周润发背后的故事——评影帝周润发鲜为人知的封神之路
  3. Word文档如何自动生成文献摘要?
  4. 计算机英语女人英语怎么说,英语时差:计算机和女人
  5. cmd设置mysql初始密码_windows下mysql初始密码设置
  6. 1、lo4j.properties
  7. doubango编码及发送流程的疑惑
  8. IDEA配置JDK源码阅读环境
  9. 单片机原理及应用 张鑫_单片机原理及应用 张鑫 课后习题答案 电子工业出版社 单片机原理及应用 张鑫 课后习题答案 电子工业出版社.doc...
  10. Lowest Common Ancestor of a Binary Tree
  11. 105道Java面试题
  12. SAP中国客户名单[转载]
  13. 训练误差与泛化误差的说明
  14. RFID手持机助力仓储物流信息化管理
  15. 计网实验:单臂路由实现VLAN之间的通信详解(Cisco Packet Tracer)
  16. 【解决】JSONDecodeError: Expecting property name enclosed in double quotes
  17. 数字unicode码
  18. cocos creator设置网络头像
  19. 【Fastapi】批量上传文件(文档、图片、视频等)
  20. 饥荒机器人雷击_饥荒机器人雷击理智_饥荒我玩机器人把雷电调的多了一点现在机器人被击中后掉精神值太多怎么防雷击我试了猪皮伞都不行_滁州气象...

热门文章

  1. 哈希函数(Hash Functions - 散列函数)的基本介绍(SHA-2,SHA-256,MD-5,Scrypt,BCrypt等)
  2. linux环境C语言操作数据库
  3. libcurl overview
  4. PHP导出成PDF【TCPDF插件】
  5. 【linux指令】dialog实现终端下的GUI-3
  6. springboot细节挖掘(知识积累)
  7. mybatis如何查询主键
  8. oracle big table1,关于Big_Table脚本的一些有关问题
  9. python的decode函数报错_python2和python3的编码encode解码decode函数
  10. matlab图像大作业,MATLAB图像大作业