我目前正在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呼叫吗?看起来我可以重新使用相同的字节数组,但我不确定如何。
任何帮助都会非常感激!这个垃圾对我很害怕!
答案 0 :(得分:4)
有well documented android bug与setPreviewCallback()
方法有关,导致过多的垃圾回收。此问题的解决方案是使用较新的方法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
一次,并通过批量方法更新其内容。