glTexImage2D中的ByteBuffer.wrap()导致过多的垃圾回收

时间:2012-07-10 04:11:05

标签: java android opengl-es garbage-collection android-camera

我目前正在android GLSurfaceView()中进行以下openGL调用onDrawFrame():

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture[0]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, camPreviewSize.width, camPreviewSize.height, 0, GLES20.GL_LUMINANCE,     GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(yArray));
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);

我需要在每个帧中执行此操作(或类似的操作),因为我正在使用相机内部使用android setPreviewCallback()功能的自定义回调方法处理来自Android相机预览的实时反馈,但我的垃圾收集是绝对疯狂(以下重复大约每秒10次!):

....

GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 18ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 22ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 25ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 20ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 20ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 16ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 22ms

....

yArray是一个字节数组,我将它包装到缓冲区。我已经使用DDMS进行了分析,实际上大多数分配都是字节数组,从我在wrap函数上完成的读取开始,似乎它可能会在调用wrap时创建一个底层byte [],这将然后在GC用作纹理后由GC收集。

如何减少分配数量?我应该以某种方式改变GL呼叫吗?看起来我可以重新使用相同的字节数组,但我不确定如何。

任何帮助都会非常感激!这个垃圾对我很害怕!

2 个答案:

答案 0 :(得分:4)

well documented android bugsetPreviewCallback()方法有关,导致过多的垃圾回收。此问题的解决方案是使用较新的方法setPreviewCallbackWithBuffer,您可以使用addCallbackBuffer方法预先分配在每个fram上添加和重新添加的缓冲区:

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
...
  mCamera.addCallbackBuffer(callbackBuffer);
  mCamera.setPreviewCallbackWithBuffer(class that implements PreviewCallback);
...
}

并且在onPreviewFrame函数中必须再次添加缓冲区:

public void onPreviewFrame(byte[] yuvArray, Camera camera) {
...
  camera.addCallbackBuffer(callbackBuffer);
...
}

解决了ByteBuffer.wrap()在GL调用中分配内存的初步怀疑:事实并非如此。它实际上将使用底层数组(在提供的代码中为yArray)并且不会分配新内存来进行垃圾回收。

答案 1 :(得分:0)

您正在每ByteBuffer次调用创建一个新的onDrawFrame实例,该实例立即被垃圾收集。只有当数据大小发生变化时才需要这个,因此纹理大小尺寸本身也是如此。我认为这不太可能。

预先分配ByteBuffer一次,并通过批量方法更新其内容。