java bytebuffer详解 - ByteBuffer.allocate()与ByteBuffer.allocateDirect()




bytebuffer使用 directbytebuffer (5)

Ron Hitches在他出色的书Java NIO中似乎提供了我认为可以很好地回答你的问题的答案:

操作系统在内存区域执行I / O操作。 就操作系统而言,这些内存区域是连续的字节序列。 因此,只有字节缓冲区才有资格参与I / O操作并不奇怪。 另请注意,操作系统将直接访问进程的地址空间,在此情况下为JVM进程,以传输数据。 这意味着作为I / O对象目标的内存区域必须是连续的字节序列。 在JVM中,字节数组可能不会连续存储在内存中,或者垃圾收集器可以随时移动它。 数组是Java中的对象,数据存储在该对象内的方式可能因JVM实现而异。

出于这个原因,引入了直接缓冲区的概念。 直接缓冲区用于与通道和本地I / O例程交互。 他们尽最大努力将字节元素存储在通道可用于直接或原始访问的内存区域中,方法是使用本机代码告诉操作系统直接排空或填充内存区域。

直接字节缓冲区通常是I / O操作的最佳选择。 按照设计,它们支持JVM可用的最高效的I / O机制。 非本地字节缓冲区可以传递给通道,但这样做可能会导致性能损失。 非直接缓冲区通常不可能成为本地I / O操作的目标。 如果将非直接的ByteBuffer对象传递给要写入的通道,则该通道可能会在每次调用时隐式执行以下操作:

  1. 创建一个临时直接的ByteBuffer对象。
  2. 将非直接缓冲区的内容复制到临时缓冲区。
  3. 使用临时缓冲区执行低级I / O操作。
  4. 临时缓冲区对象超出范围并最终被垃圾收集。

这可能会导致缓冲区复制和每个I / O上的对象流失,这正是我们想要避免的事情。 但是,根据实施情况,情况可能不会那么糟糕。 运行时可能会缓存和重用直接缓冲区或执行其他巧妙的技巧来提高吞吐量。 如果您只是创建一次性使用的缓冲区,则差异不显着。 另一方面,如果您将在高性能场景中重复使用缓冲区,则最好分配直接缓冲区并重新使用它们。

直接缓冲区对于I / O是最佳的,但是它们的创建可能比非直接字节缓冲区更昂贵。 直接缓冲区使用的内存通过调用本地操作系统特定的代码来分配,绕过标准的JVM堆。 根据主机操作系统和JVM实现的不同,设置和拆除直接缓冲区可能比堆驻留缓冲区贵得多。 直接缓冲区的内存区不受垃圾回收处理,因为它们不在标准JVM堆中。

使用直接缓冲区和非直接缓冲区的性能权衡取决于JVM,操作系统和代码设计。 通过在堆外部分配内存,您可能会将应用程序置于JVM不知道的其他外力之下。 在发挥其他运动部件的作用时,确保达到预期的效果。 我推荐以前的软件格言:首先让它工作,然后让它快速。 先不要过分担心优化; 首先要关注正确性。 JVM实现可能能够执行缓冲区缓存或其他优化,这将为您提供所需的性能,而无需您付出大量不必要的努力。

allocate()allocateDirect() ,这就是问题所在。

多年来,我一直坚持认为,因为DirectByteBuffer是在操作系统级别的直接内存映射,所以它的get / put调用比HeapByteBuffer更快。 我从来没有真正感兴趣的是直到现在才找到有关情况的确切细节。 我想知道两种类型的ByteBuffer的哪一种更快,以及在什么条件下。


因为DirectByteBuffers是操作系统级别的直接内存映射

他们不是。 它们只是普通的应用程序进程内存,但不受Java GC期间的重定位影响,这大大简化了JNI层中的内容。 你所描述的适用于MappedByteBuffer

它会更快地执行get / put调用

结论并不遵循前提; 前提是错误的; 结论也是错误的。 一旦进入JNI层,它们会更快,并且如果您正在读取和写入同一个DirectByteBuffer它们会更快,因为数据永远DirectByteBuffer跨越JNI边界。



没有理由期望直接缓冲区在jvm 访问更快。 当你将它们传递给本地代码时,它们的优势就来了 - 例如各种渠道背后的代码。


I will try to answer using practical scenario to show the distinction between the two.

Interfaces come with zero payload ie no state has to be maintained and thus are better choice to just associate a contract (capability) with a class.

For example, say I have a Task class that performs some action, now to execute a task in separate thread I don't really need to extend Thread class rather better choice is to make Task implement Runnable interface (ie implement its run() method) and then pass object of this Task class to a Thread instance and call its start() method.

Now you can ask what if Runnable was a abstract class?

Well technically that was possible but design wise that would have been a poor choice reason being:

  • Runnable has no state associated with it and neither it 'offers' any default implementation for the run() method
  • Task would have to extend it thus it couldn't extend any other class
  • Task has nothing to offer as specialization to Runnable class, all it needs is to override run() method

In other words, Task class needed a capability to be run in a thread which it achieved by implementing Runnable interface verses extending the Thread class that would make it a thread.

Simply put us interface to define a capability (contract), while use a abstract class to define skeleton (common/partial) implementation of it.

Disclaimer: silly example follows, try not to judge :-P

interface Forgiver {
    void forgive();
}

abstract class GodLike implements Forgiver {
    abstract void forget();
    final void forgive() {
        forget();
    }
}

Now you have been given a choice to be GodLike but you may choose to be Forgiver only (ie not GodLike) and do:

class HumanLike implements Forgiver {
    void forgive() {
       // forgive but remember    
    }
}

Or you may may choose to be GodLike and do:

class AngelLike extends GodLike {
    void forget() {
       // forget to forgive     
    }
}

PS with java 8 interface can also have static as well default (overridable implementation) methods and thus difference b/w interface and abstract class is even more narrowed down.





java performance nio bytebuffer