[JAVA学习笔记-60]ByteBuffer的分配方式的比较

来源:互联网 发布:windows xp系统是什么 编辑:程序博客网 时间:2024/05/29 04:09

public static ByteBuffer allocate(int capacity)  

public static ByteBuffer allocateDirect(int capacity)  

方法1从JVM进程的堆上分配内存,分配的ByteBuffer可被GC标记并回收;

方法2调用系统的native方法分配内存,分配的ByteBuffer对象不能被GC回收,需要

     手工释放,使用这种内存进行I/O,好处就是buffer中的数据,不需要再由系统

     内存拷贝到JVM内存中,而是被JAVA进程直接访问,节约了一步拷贝的过程。

 

性能比较:


可见在1MB的buffer大小下,directBuffer并没有明显的性能优势

 

应用场景:

ByteBuffer:

       适用于大多数的场景,除非测试证明有性能问题,否则尽量使用此对象。

DirectBuffer:

       当遇到性能瓶颈时考虑使用;

       适用于作为访问频繁,且生命周期较长的缓存(猜测是申请时比较耗时,因为不适合频繁申请的场景);

       The newI/O (NIO) classes in java.nio allow the creation and use of direct buffers. These bufferstremendously increase throughput for repeated I/O activities. However, theircreation and reclamation is more expensive than the creation and reclamation ofheap-based non-direct buffers because direct buffers are managed usingOS-specific native code.

 

 

以下为stackoverflow一个帖子里的摘录,来自 JAVA NIO 这本书

Operating systems perform I/O operationson memory areas. These memory areas, as far as the operating system isconcerned, are contiguous sequences of bytes. It's no surprise then that onlybyte buffers are eligible to participate in I/O operations. Also recall thatthe operating system will directly access the address space of the process, inthis case the JVM process, to transfer the data. This means that memory areasthat are targets of I/O perations must be contiguous sequences of bytes. In theJVM, an array of bytes may not be stored contiguously in memory, or the GarbageCollector could move it at any time. Arrays are objects in Java, and the waydata is stored inside that object could vary from one JVM implementation toanother.

For this reason, the notion of a directbuffer was introduced. Direct buffers are intended for interaction with channelsand native I/O routines. They make a best effort to store the byte elements ina memory area that a channel can use for direct, or raw, access by using nativecode to tell the operating system to drain or fill the memory area directly.

Direct byte buffers are usually the bestchoice for I/O operations. By design, they support the most efficient I/Omechanism available to the JVM. Nondirect byte buffers can be passed tochannels, but doing so may incur a performance penalty. It's usually not possiblefor a nondirect buffer to be the target of a native I/O operation. If you passa nondirect ByteBuffer object to a channel for write, the channel mayimplicitly do the following on each call:

1.    Create a temporary direct ByteBufferobject.

2.    Copy the content of the nondirect bufferto the temporary buffer.

3.    Perform the low-level I/O operationusing the temporary buffer.

4.    The temporary buffer object goes out ofscope and is eventually garbage collected.

This can potentially result in buffercopying and object churn on every I/O, which are exactly the sorts of thingswe'd like to avoid. However, depending on the implementation, things may not bethis bad. The runtime will likely cache and reuse direct buffers or performother clever tricks to boost throughput. If you're simply creating a buffer forone-time use, the difference is not significant. On the other hand, if you willbe using the buffer repeatedly in a high-performance scenario, you're betteroff allocating direct buffers and reusing them.

Direct buffers are optimal for I/O, butthey may be more expensive to create than nondirect byte buffers. The memoryused by direct buffers is allocated by calling through to native, operatingsystem-specific code, bypassing the standard JVM heap. Setting up and tearing downdirect buffers could be significantly more expensive than heap-residentbuffers, depending on the host operating system and JVM implementation. Thememory-storage areas of direct buffers are not subject to garbage collectionbecause they are outside the standard JVM heap.

The performance tradeoffs of using direct versusnondirect buffers can vary widely by JVM, operating system, and code design. Byallocating memory outside the heap, you may subject your application toadditional forces of which the JVM is unaware. When bringing additional movingparts into play, make sure that you're achieving the desired effect. Irecommend the old software maxim: first make it work, then make it fast. Don'tworry too much about optimization up front; concentrate first on correctness.The JVM implementation may be able to perform buffer caching or otheroptimizations that will give you the performance you need without a lot ofunnecessary effort on your part.

 

问题:

1、        内存泄漏

因为DirectBuffer不能被GC回收,因此需要手动释放,否则会有内存泄漏。

 

2、        源码解析

DirectBuffer的内存分配:

 


通过unsafe.allocateMemory 分配内存,这是一个native方法,依赖本地实现,

在linux上是调用malloc申请一块内存。Pagesize()也是native方法,调用本地

方法,获取一个page的大小(2的整数倍)

         申请的大小是capacity+pagesize,用来存放附加信息?

注意这里通过Cleaner的静态方法,创建了一个cleaner对象,cleaner包含了

一个Deallocator对象,用于内存回收。

 

DirectBuffer的内存回收:

         通过Cleaner.clean(),回收内存;



可见clean实际是启动了thunk的线程运行,而thunk实际就是Deallocator,它是

一个实现了Runnable接口的对象。


Deallocator调用freeMemory的native方法,释放内存。

 

3、        DirectBuffer从哪里申请内存

DirectBuffer使用的是native memory,即,OS所使用的内存空间,此内存空间

不与JVM进程使用的内存空间重叠(大小由JVM的参数 –xMx控制),因此native

Memory实际上不能被JVM的GC所回收。

 

申请native memory,系统开销更大,通常需要200条CPU指令,而申请heap

Memory只需要20条左右的CPU指令。

 

关于JVM对native memory的使用,可参考文章:Thanksfor the memory, Linux

 

0 0
原创粉丝点击