您的位置:首页 > 编程语言 > Java开发

Java虚拟机高耗内存的一种考虑

2016-05-17 10:02 323 查看
在工程中发现,即便设置了虚拟机的最大堆内存大小,可是还是会发生虚拟机JAVA进程耗用大量内存的情况,比如设置为最大5G,实际可能是十几G,其中一个原因是,Java虚拟机可以在堆外分配内存。这里提供了一个限制堆外分配内存的一种可能的方式:

通过-XX:MaxDirectMemorySize=<size>

使用该参数可能会面临2个问题:

1、若直接内存分配大小设置过小,虚拟机是否会因缺少资源而崩溃;

2、者性能受到很大的抑制。

该参数未经验证是否有效,待有机会时再进行验证。

参考:

-----------------------------------------------------------------

转载-Non-direct与direct ByteBuffer区别

标签:
虚拟机java网络referencejnibuffer

2012-03-29 15:52
2311人阅读 评论(1)
收藏
举报

转载于:http://crmky.spaces.live.com/Blog/cns!8C989768DB1A6B14!458.entry?sa=254330365这两种类型的ByteBuffer相信大家都知道,但是两者的区别在什么地方呢?在不同的环境下采用哪种类型的ByteBuffer会更有效率呢?

先解释一下两者的区别:

Non-directByteBuffer内存是分配在堆上的,直接由Java虚拟机负责垃圾收集,你可以把它想象成一个字节数组的包装类,如下伪码所示:

HeapByteBuffer extends ByteBuffer {

byte[] content;

int position, limit, capacity;

......

}

而DirectByteBuffer是通过JNI在Java虚拟机外的内存中分配了一块(所以即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的DirectByteBuffer),该内存块并不直接由Java虚拟机负责垃圾收集,但是在DirectByteBuffer包装类被回收时,会通过Java
Reference机制来释放该内存块。如下伪码所示:

DirectByteBuffer extends ByteBuffer {

long address;

int position, limit, capacity;

protected void finalize() throws Throwable{

//释放内存块,该段代码仅仅用于演示,真正的DirectByteBuffer并不是通过finalize来释放的

releaseAddress();

......

}

......

}

我相信大部分朋友们对上面的区别都应该很了解,那么还有什么其他的区别呢?嘿嘿,让我们稍微深入一点,翻到sun.nio.ch.IOUtil.java,绝大部分Channel类都是通过这个工具类和外界进行通讯的,如FileChannel/SocketChannel等等。我简单的用伪码把write方法给表达出来(read方法也差不多,就不多做说明了)

int write(ByteBuffer src, ......) {

if (src instanceof DirectBuffer)

return writeFromNativeBuffer(...);

ByteBufferdirect = getTemporaryDirectBuffer(src);

writeFromNativeBuffer(direct,......);

updatePosition(src);

releaseTemporaryDirectBuffer(direct);

}

是的,在发送和接收前会把Non-directByteBuffer转换为DirectByteBuffer,然后再进行相关的操作,最后更新原始ByteBuffer的position。这意味着什么?假设我们要从网络中读入一段数据,再把这段数据发送出去的话,采用Non-directByteBuffer的流程是这样的:

网络 --> 临时的DirectByteBuffer --> 应用Non-directByteBuffer --> 临时的DirectByteBuffer
--> 网络

而采用DirectByteBuffer的流程是这样的:

网络 --> 应用 DirectByteBuffer --> 网络

可以看到,除开构造和析构临时DirectByteBuffer的时间外,起码还能节约两次内存拷贝的时间。那么是否在任何情况下都采用DirectBuffer呢?

不是。对于大部分应用而言,两次内存拷贝的时间几乎可以忽略不计,而构造和析构DirectBuffer的时间却相对较长。在JVM的实现当中,某些方法会缓存一部分临时DirectByteBuffer,意味着如果采用DirectByteBuffer仅仅能节约掉两次内存拷贝的时间,而无法节约构造和析构的时间。就用Sun的实现来说,write(ByteBuffer)和read(ByteBuffer)方法都会缓存临时DirectByteBuffer,而write(ByteBuffer[])和read(ByteBuffer[])每次都生成新的临时DirectByteBuffer。

根据这些区别,我会提出如下的建议:

· 如果你做中小规模的应用(在这里,应用大小是按照使用ByteBuffer的次数和规模来做划分的),而且并不在乎这该死的细节问题,请选择Non-directByteBuffer· 如果采用DirectByteBuffer后性能并没有出现你所期待的变化,请选择Non-directByteBuffer· 如果没有DirectByteBuffer
Pool,尽量不要使用DirectByteBuffer· 除非你确定该ByteBuffer会长时间存在,并且和外界有频繁交互,可采用DirectByteBuffer· 如果采用Non-directByteBuffer,那么采用非聚集(gather)的write/read(ByteBuffer)效果反而可能超出聚集的write/read(ByteBuffer[]),因为聚集的write/read的临时DirectByteBuffer是非缓存的

基本上,采用Non-directByteBuffer总是对的!因为内存拷贝需要的开销对大部分应用而言都可以忽略不计。不过我做的是大规模的网络并发框架,因此对这些细节问题还是有必要有深入认识的,并且根据这些细节来调节自己的Buffer继承体系(再次抱怨,ByteBuffer无法扩展实在是一个非常非常非常费解的设计)

注:前面提到的“即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的DirectByteBuffer”中的可能是指可以通过-XX:MaxDirectMemorySize=<size>来指定DirectByteBuffer实例最多可以使用的内存总数。如指定-XX:MaxDirectMemorySize=1024,则系统中所有存活的DirectByteBuffer总内存数不能超过1024字节。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: