如何扩展字节缓冲区的分配内存

Posted

技术标签:

【中文标题】如何扩展字节缓冲区的分配内存【英文标题】:How to extend the allocated memory of a bytebuffer 【发布时间】:2014-04-22 16:03:49 【问题描述】:

我有一个字节缓冲区,我放了 Ints、Chars 等。 因为我不知道我需要多少空间,所以我想动态增长字节缓冲区。这是怎么做到的?

示例: - 我有 2 个字节的 Bytebuffer - 我在字节缓冲区中添加了一个字符(现在字节缓冲区已满) - 我喜欢通过将字节缓冲区扩展为 4 个字节来向字节缓冲区添加一个整数。我无法在开始时分配 6 个字节的字节缓冲区。

    ByteBuffer byteBuffer = ByteBuffer.allocate(2);

    byteBuffer.putChar('a');
    byteBuffer.putInt(1);

我印象深刻,有多少人正在处理我几分钟前的问题。非常感谢大家,也感谢 ***,这是一个很棒的平台!

你们都问,我在做什么。所以我试着在这里解释一下。 我的用例: 我将结构化数据表示为 javaobjects (javaclasses),我想将这些数据存储到数据库中并从数据库中读取。阅读速度应该很快。 到目前为止我做了什么:

java 序列化和反序列化并将其存储在一个 blob 中 -> 运行良好但速度太慢。

尝试了几个 3rd 方序列化程序,如 kryo(非常好),但在我的 案例(机器人)。

我的新策略 :-) : 我自己对我的班级进行外化。为此,我想将我的类顺序的整个数据构造为字节数组。这可能很慢。然后我将字节数组存储到我的数据库中的一个 blob 中。阅读时,我想一次读取字节数组(字节数组约为 10k)。 (我会有很多)。然后解析字节数组以提取结构化数据。

我认为使用字节缓冲区非常适合这样做,因为 putX 和 readX 之类的方法? (X 表示字符、浮点数、整数)

【问题讨论】:

为什么不使用不同的数据结构呢?如果您想要动态重新分配,为什么不使用链表? 为什么不能分配容量为 4 或 1024 字节或 1 MB 的 ByteBuffer? 顺便说一句 putChar() 不经常使用,可能有更好的方法来编写字符,具体取决于您这样做的原因。 你的答案真的没有帮助:-)。为什么我要求这个,我可以从一开始就分配 1024。 @user1344545 我们正试图了解您为什么要这样做。如果您想要一个替代解决方案,知道什么是有用的会很有用。 【参考方案1】:

你不能。这是设计使然。您可以分配一个新的字节缓冲区并将溢出的数据写入其中。您可以将 ByteBuffers 保存在 LinkedList 中(它将根据需要增长),如果您的内存不足来分配新的内存,您甚至可以将旧的缓存器放到磁盘上。如果每个 ByteBuffer 的大小相同,那么简单的方程式可以让您访问它,就好像它只有一个缓冲区一样,但是您将失去使用切片或压缩的能力,或者您可以用其中之一做的任何很酷的事情。 :)

但就像人们一遍又一遍地说的那样,这取决于您需要它的用途。

【讨论】:

我不确定你的意思?【参考方案2】:

恕我直言,最好的答案是从一开始就确保您有足够的空间。让您的 ByteBuffer 动态调整大小非常昂贵且速度较慢。

ByteBuffer byteBuffer = ByteBuffer.allocate(6 /* or more */);

byteBuffer.putChar('a');
byteBuffer.putInt(1);

用于字符的最简单缓冲区是 StringBuilder。

StringBuilder sb = new StringBuilder();
sb.append('a');
sb.append('b');

你会如何在你最后的陈述之后添加一个新角色?

sb.append('n');

只有当你完成时。

// if you need a ByteBuffer
ByteBuffer bb = ByteBuffer.wrap(sb.toString().getBytes(StandardCharsets.UTF_8));
// bb will have two bytes for 'a' and 'b'

但是,如果您要附加到 ByteBuffer(而不是使用字符),我建议使缓冲区比您应该需要的大,因此您永远不需要调整它的大小。如果你担心使用堆,你可以使用非堆代替。

// uses about 48 bytes of heap.
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024);

bb.putInt(1234);
bb.putDouble(1.111);
bb.putLong(12345678987654321L);

【讨论】:

+1 这是一个更好的解决方案,因为它允许动态重新分配。 不,那不是灵魂。你会如何在你的最后一句话之后添加一个新角色? @user1344545 我在答案中添加了另一个字符。 非常感谢您的努力,但这对我没有用:-) 我扩展了我的问题以显示,附加到字符串缓冲区没有帮助。我真的想在开始时只分配 2 个字节并将 Ints、Chars、Floats 动态添加到缓冲区中。 我通过将第二个 put 更改为 int 来扩展我的示例。【参考方案3】:

使用字节缓冲区的问题在于您从固定大小开始。如果您知道要保存多少数据,那就太好了。例如,如果您正在读取输入并且只想读取 12 个字节,则只创建一个 12 字节的数组。

如果您不知道字节缓冲区中有多少数据,您可以做 2 件事:

    如果您需要更快的速度,那么只需根据您需要多少数据的预测来分配一个大数组。例如,如果您正在读取一个大文件并且想要快速读取,那么您应该使用一个非常大的字节缓冲区(根据文件的大小可能有几 MB)。

    李>

    使用使用动态分配的结构。这是更好的解决方案,但速度较慢。如果您分配非常大的数组,那么这将非常慢,但不会浪费任何内存。如果您使用一个字节缓冲区并分配 512 KB,但您只使用 1KB,那将浪费大量空间!使用动态分配的结构(LinkedList、Stack、Queue、Trees),您可以根据需要添加和删除元素

最后的解决方案,完全不推荐,因为它不仅浪费很多内存,而且速度也很慢。您可以根据需要在字节缓冲区中分配尽可能多的空间,然后当您需要更多内存时,创建一个新缓冲区并复制数据。这就是您正在尝试做的事情,而且效率非常低。

在我看来,Peter Lawrey 的答案是解决此问题的绝佳方法,因为您可以轻松地从 StringBuilder 转到字节数组。这具有您需要的所有效率和速度。

【讨论】:

+1 BTW 2 个字节的 byte[] 和 6 个字节的 byte[] 使用相同数量的内存(在 64 位 JVM 上)。通过将尺寸设为 2 而不是 6,您不会节省任何费用。【参考方案4】:

我为我的用例找到了一个解决方案,我想在这里简要介绍一下。 我接受了 Mikkel 的答案,因为关于这个问题,我想这是正确的答案,这也是 John 提出的。还要感谢 Peter 的精彩解释,我可以从中学习。

我使用DataInputStream,这对我的情况很方便。 我使用 DataInputStream 和方便的方法(如 putInt、PutBoolean、PutString )基于我的 Java 对象创建数据流。然后我得到 DataInputStream 的二进制数组并将其作为 blob 存储到我的数据库中。

读取与DataInputStream正好相反。

与我的 java 对象的 java 反序列化相比,我在读取对象方面的性能提高了 500%,并且存储使用量减少了 25%。

【讨论】:

【参考方案5】:

如果您将缓冲区实例保存在变量中,则可以。 例如像这样(kotlin):

fun ShortBuffer?.clone(addLimit: Int = 0): ShortBuffer? 
    if (this == null) 
        return ShortBuffer.allocate(addLimit)
     else 
        val copy = ShortBuffer.allocate(this.limit() + addLimit)
        copy.put(this.array())
        return copy
    


fun ShortBuffer?.safePut(shortArray: ShortArray?): ShortBuffer? 
    if (this == null) 
        if(shortArray == null) 
            return null
         else 
            val ret = ShortBuffer.allocate(shortArray.size)
            ret.put(shortArray)
            return ret
        
     else if (shortArray == null) 
        return this
     else if (this.remaining() < shortArray.size) 
        val ret = clone(shortArray.size-this.remaining())
        ret?.put(shortArray)
        return ret
     else 
        this.put(shortArray)
        return this
    

用法:

var buff = ShortBuffer.allocate(0)
buff = buff.safePut(shortArrayOf(1,2,3))

【讨论】:

以上是关于如何扩展字节缓冲区的分配内存的主要内容,如果未能解决你的问题,请参考以下文章

在实时环境中使用 C++ 预分配内存

iOS Instrumentation:如何解释内存分配模板?

“私人字节”没有反映出来。如何找到进程分配的确切内存?

如何在 C++ 中急切提交分配的内存?

JVM 进程如何分配其内存?

堆缓冲区溢出试图重新分配内存。 C语言