You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用allocate()与allocateDirect()分配ByteBuffer的数据异常问题咨询

ByteBuffer.allocate()与allocateDirect()的YUV数据填充问题

问题场景

尝试模拟拼接YUV_420_888图像ByteBuffer,使用allocate()分配缓冲区时,填充左半部分为固定值125能得到正确结果;但替换为allocateDirect()后,数据出现错乱(左侧为allocateDirect()的错误结果,右侧为allocate()的正确结果),且通过array().length获取大小为391(预期为384)。填充代码如下:

int width = 16;
int height = 16;
int combinedSize = width * height * 3 / 2;
ByteBuffer combinedBuffer = ByteBuffer.allocate(combinedSize);
for(int i = 0; i < height * 3 / 2; i++) {
    int curLine =  i * width;
    for(int j = 0; j < width / 2; j++) {
        combinedBuffer.put(curLine + j, (byte) 125);
    }
}

核心原因分析

1. array()方法的本质差异

  • allocate()创建的是堆内缓冲区:底层基于JVM堆内的byte[]实现,调用array()会直接返回这个数组,长度严格等于分配的combinedSize=384。
  • allocateDirect()创建的是堆外直接缓冲区:存储在操作系统管理的本地内存中,没有对应的堆内byte[]。按Java规范,直接缓冲区调用array()应抛出UnsupportedOperationException,但部分JVM实现会返回一个临时拷贝的数组,其长度会因本地内存对齐要求(如页大小、硬件对齐规则)被填充到非预期值(如391),这并非缓冲区的有效数据长度。

2. 数据读取错误导致图像异常

你通过array()获取直接缓冲区数据并写入文件,相当于把包含冗余填充字节的临时数组写入,而非缓冲区实际的384字节有效数据,直接导致YUV图像格式错乱。

正确解决方案

无论使用哪种缓冲区,都应通过标准API读取数据,避免依赖array():

  1. 重置缓冲区读取位置:
    combinedBuffer.position(0); // 或调用flip(),若之前有写入操作
    
  2. 创建指定大小的数组,拷贝缓冲区有效数据:
    byte[] validData = new byte[combinedSize];
    combinedBuffer.get(validData);
    // 将validData写入文件即可得到正确的YUV数据
    

填充数据时的put(int index, byte b)方法对两种缓冲区行为一致,只要索引在有效范围内,数据写入是正确的,问题仅出在后续读取环节。

额外注意事项

直接缓冲区的内存不由JVM垃圾回收自动释放,需手动调用clean()(通过ByteBuffer.cleaner()获取Cleaner实例)或依赖JVM最终化机制,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Gabriel Liu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:57:41