【问题标题】:JAVA BufferOverflowException, while keeping buffer capacity?JAVA BufferOverflowException,同时保持缓冲区容量?
【发布时间】:2016-06-25 21:51:18
【问题描述】:

所以我有一个从 ByteBuffer 创建 IntBuffer 的方法:

public static IntBuffer directIntBuffer(int[]buffer){
        ByteBuffer bb = ByteBuffer.allocateDirect(4*buffer.length);
        bb.order(ByteOrder.nativeOrder());
        IntBuffer ib = bb.asIntBuffer();
        ib.put(buffer).flip();
        return ib;
    }

在另一个类的构造函数中,我正在初始化其中一个 IntBuffers 并用嵌套的 for 循环填充它:

编辑: 所以这是我的完整构造函数。我用实际的顶点坐标填充 FloatBuffer FBVertices,用索引填充 IntBuffer IBVerticesIndex。后来,我使用 GL11.glDrawElements(GL11.GL_TRIANGLES, IBVerticesIndex); 绘制这些;

public sphereHelper(float fRadius, int iSlices, int iStacks)

{  
    FBVertices = GLDrawHelper.directFloatBuffer(new float[iSlices*iStacks*4*3]); 
    FBVertices.clear();

    IBVerticesIndex = GLDrawHelper.directIntBuffer(new int[iSlices*iStacks*4]);
    IBVerticesIndex.clear();
    int index=0;




    float verticalDegreePerStack= (float)Math.PI/iStacks; //1*Pi because we only want the height of the sphere once (from -PI/2 to PI/2)
    float horizontalDegreePerSlice = 2.0f* (float)Math.PI/iSlices; //2*Pi because of unit circle/polar coordinates going once around the sphere

    for (int k=0; k< iStacks; k++)
    {
        //overall vertical angle
        float rho= (float)k*verticalDegreePerStack;
        //sinus of that angle
        float srho =(float) (Math.sin(rho));

        float crho = (float) (Math.cos(rho));

        //sinus and cosinus of vertical angle+ angle of 1 stack
        float srhodrho= (float) (Math.sin(rho + verticalDegreePerStack));
        float crhodrho = (float) (Math.cos(rho + verticalDegreePerStack));

        for (int j=0; j<iSlices;j++)
        {
            //overall angle along the horizontal border (0 degree at full 2*PI circle)
            float theta = (j==iSlices)? 0.0f : j*horizontalDegreePerSlice;
            //sinus and cosinus
            float stheta = (float)(-Math.sin(theta));
            float ctheta = (float)(Math.cos(theta));

            //coordinates of first vertex of current triangle
            float x=stheta*srho;
            float z=ctheta*srho;
            float y=crho;


            //put coordinates of first vertex into buffer
            FBVertices.put(new float[]{x*fRadius,y*fRadius,z*fRadius});

            //put index of first vertex of polygon in index array, increase index counter by one
            if (IBVerticesIndex.position()<IBVerticesIndex.capacity())
            {IBVerticesIndex.put(index);}
            index++;
            //second vertex
            x = stheta*srhodrho;
            z = ctheta*srhodrho;
            y = crhodrho;

            //put these coordinates into Buffer
            FBVertices.put(new float[] {x*fRadius,y*fRadius,z*fRadius});


             if (IBVerticesIndex.position()+2<IBVerticesIndex.capacity())
            {
            IBVerticesIndex.put(new int[] {index,index+1,index+2});}
            index++;

            System.out.println(IBVerticesIndex.position()+"/"+IBVerticesIndex.capacity());

        }
        IBVerticesIndex.flip();
        FBVertices.flip();

       }
    }

我得到一个 BufferOverflowException,在这个输出:

4/400 8/400 12/400 16/400 20/400 24/400 28/400 32/400 36/400 40/400

线程“主”java.nio.BufferOverflowException 中的异常

所以它在缓冲区容量的十分之一处溢出? 我在开始时 clear() 缓冲区,所以应该重置位置。

感谢您的意见!

【问题讨论】:

  • 无法重现,每次运行您的代码时都会得到400/400 作为输出。
  • IBVerticesIndex 在您的代码中是什么类型(我只是将其声明为IntBuffer)...您可以编辑您的帖子并包含周围的代码吗?您如何准确地获得这些输出?哦,您的 println 位于内部循环中,而不是您将其放在示例代码中的位置。而且你错过了一些卷曲......请清理那个烂摊子,这显然让您的问题的至少一位读者感到困惑:P 仍然无法重现,我以 4 步获得从 4/400 到 400/400 的所有输出.你的问题在别处。你在index 的声明和循环之间省略了哪些代码?
  • 是的,很抱歉造成混乱。当我试图在这里为球体的顶点填充 OPENGL 索引缓冲区时,我删除了可能不相关的部分。您对大括号是正确的, println 应该在循环之外。我将用我的代码更新问题。
  • 从 Buffer 超类中,flip() 使缓冲区准备好进行新的通道写入或相对获取操作序列:它将限制设置为当前位置,然后将位置设置为零。 这也意味着您在每次外部循环迭代时都会覆盖缓冲区中的数据,因为位置设置为 0。我真诚地怀疑这就是您在此构造函数中想要的吗?
  • 就是这样! flip() 不应该在循环内。当您使用如此多的代码时,您会对这些小细节视而不见。谢谢你们俩。

标签: java buffer-overflow bytebuffer


【解决方案1】:

编辑: +1 对 Arjan 对原始问题的评论。我将 Don 的代码复制到我的机器上,它在 Java 8 上运行,也得到 400/400 作为输出。您是否在注释掉的代码中的其他位置设置了limit?

您是否阅读过Buffer superclass 的工作原理?具体来说,该类有一个 limit 属性,该属性没有详细记录的初始值。超过limit 值会抛出BufferOverflowException。

在我看来,您想像使用原始数组一样使用此IntBuffer,在这种情况下,在您的初始化例程中,您需要在调用flip() 之后将limit 设置为capacity,根据到文档将limit 重置为mark。

【讨论】:

  • 不符合我的观察。请求和比较位置()和容量()不应该影响指针/限制对吧?我读到每个 ByteBuffer 上都有一个写入和一个读取 mdoe,并且限制仅等于写入模式下的容量。根据我的理解,调用 .clear() 应该将其设置为写入模式。如果不是太多,您可以测试整个代码以及它是否产生相同的输出?
  • 我复制了你的新代码,我得到了和你相似的输出。在我的屏幕输出中似乎多次达到 40/400。还在看。
  • 发现问题了,flip() 每次循环都会重置缓冲区指针。谢谢!
  • 将代码移到下一个大括号之外似乎可行,因为我现在得到了 400/400 输出。最后要注意的是 - 分配 400 个int 原语意味着 1600 个字节。根据您的网格最终的复杂程度,您可以考虑使用unsigned short 来节省一些内存。 Java 不提供此功能,但我相信 Google Guava 库提供。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-22
  • 2023-02-04
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多