JNI - Free ByteBuffer from C ++

Summary

  • Create a ByteBuffer in Java called a buffer with ByteBuffer.allocateDirect (someBufferSize)
  • Fill the buffer with data
  • Pass buffer in C ++ as jobject - jbuffer
  • Get direct pointer to buffer with env-> GetDirectBufferAddress (jbuffer)
  • Working with buffer data on the C ++ side. How can we prevent GC from flushing our buffer or it never happens?
  • Job done - we don't need jbuffer.
  • Release jbuffer? free (jbuffer) - Will result in an invalid address error.

Long part

I am using the following code to load PNG files with Java AssetManager to use them to generate Open GL ES 2.0 textures.

Java class PNG

import java.nio.ByteBuffer;
import android.graphics.Bitmap;

public class PNG 
{
 private static final int BYTES_PER_PIXEL_PNG = 4;
 private static final String LOG_TAG = "[PNG]";
 public int width;
 public int height;
 public ByteBuffer pixels;


 public PNG(Bitmap bitmap)
 {
  this.width = bitmap.getWidth();
  this.height = bitmap.getHeight();
  this.pixels = ByteBuffer.allocateDirect(this.width * this.height * BYTES_PER_PIXEL_PNG);
  bitmap.copyPixelsToBuffer(this.pixels);
 }
}

public static PNG loadPNG(String path)
{
    InputStream is = null;
    try
    {
        is = ASSETS_MANAGER.open(path);//get png file stream with AssetsManager instance
    }
    catch (IOException e)
    {
        Log.e(LOG_TAG, "Can't load png - " + path, e);
    }

    return new PNG(BitmapFactory.decodeStream(is));
}

      

C ++ side PNG

typedef struct png
{
int width;
int height;
char* pixels;
} png;

png* load_png(const char* path)
{
 png* res = (res*) malloc(sizeof(png);
 ...
 jobject _png = env->CallStaticObjectMethod(get_java_lib_class(), get_method_id(JAVA_LIB_LOAD_PNG, JAVA_LIB_LOAD_PNG_SIGN), _path);//Calling loadPng() from Java, get PNG jobject
 jobject _pixels =  env->GetObjectField(_png, PNG_FIELDS->PNG_PIXELS_ID);//Getting pixels field from Java PNG jobject
 res->pixels = (char*) env->GetDirectBufferAddress(_pixels);//Get direct pointer to our pixel data
 //Cleanup
 ...
 env->DeleteLocalRef(_png);
 env->DeleteLocalRef(_pixels);
 return res;
}

      

Then using png to create the texture

void test_create_tex(const char* path)
{
 ...
 png* source = load_png(path);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source->width, source->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, source->pixels);
//We don't need source pixel data any more
//free(source->pixels);//INVALID HEAP ADDRESS (deadbaad)
free(source);
}

      

So how to free the byte buffer after using on the C ++ side with a direct pointer? It is allocated directly (like malloc is native) and must be deallocated or I will get an OutOfMemory error.

+3


source to share


2 answers


You don't need to free the buffer. You have isolated it on the Java side, which means the JVM object and GC will take care of this. In contrast to the allocation on the C side, it is thus a native object that the GC does not know about. You don't even need to do this DeleteLocalRef

, as all local references will be removed for you by the JNI engine after the native method returns. You will only need to explicitly delete if hundreds of JNI calls are returned to the JVM within a single native call, so you run out of handles even before returning to the JVM.



I must admit that I don't know exactly how the GC knows it shouldn't touch your ByteBuffer, but I would suggest that by calling GetObjectField

, you are increasing the refcount by the ByteBuffer and decreasing with DeleteLocalRef

. So it is safe to stay between these two JNI calls to ByteBuffer.

+3


source


In my opinion, you don't need to worry about the release ByteBuffer pixels

because it is driven by the JVM. What you really need is to keep it from being garbage collected when using C ++.



+2


source







All Articles