2013-02-07 60 views
3

摘要JNI - 免費的ByteBuffer從C++

  1. 創建的ByteBuffer在Java中稱爲緩衝區ByteBuffer.allocateDirect(someBufferSize)與數據
  2. 通行證緩衝到C++作爲jobject
  3. 填充緩衝 - jbuffer
  4. 使用env-> GetDirectBufferAddress(jbuffer)獲取緩衝區直接指針
  5. 使用C++端的緩衝區數據。如何防止GC清理我們的緩衝區或它永遠不會發生?
  6. 完成工作 - 我們現在不需要jbuffer。
  7. 發佈jbuffer?免費(jbuffer) - 將引發一個無效地址錯誤

長的部分

我用下面的代碼加載與Java AssetManager PNG文件使用它們的Open GL ES 2.0紋理的創建。

Java端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++側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; 
} 

然後,使用PNG來創建紋理

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); 
} 

那麼在C++端使用它的直接指針後如何釋放字節緩衝區呢?它是直接分配的(就像malloc - 在本機端),必須釋放,否則我會得到OutOfMemory錯誤。

+1

與http://stackoverflow.com/questions/1246983/when-and-how-are-nio-direct-buffers-freed – Raedwald

回答

3

您不需要釋放緩衝區。你已經在Java端分配了它,這意味着它是JVM對象,GC將會關心它。與C端分配相反,這是GC不知道的本地對象。您甚至不需要執行DeleteLocalRef,因爲從本機方法返回時,JNI機器將刪除所有本地引用。只有當一個本地調用的範圍內有數百個JNI調用返回到JVM時,才需要顯式刪除,因此即使在返回到JVM之前,您也可能會耗盡處理。

我必須承認,我不知道GC究竟是如何知道它不應該碰你的ByteBuffer,但我猜想,通過調用GetObjectField你增加對ByteBuffer的引用計數,並與DeleteLocalRef遞減。因此,在這兩個JNI調用之間,ByteBuffer是安全的。

+0

密切相關其實,它是部分錯誤的。當你創建一個直接的NIO緩衝區時,它的一小部分被分配到由垃圾回收器管理的Java堆上,並且包含你的數據的部分被分配到本地堆上。當沒有足夠的本地內存來創建新的直接NIO緩衝區時,有一種非常弱的機制試圖懇求垃圾回收器,但最好考慮對本地內存的管理由開發人員來決定。我建議你閱讀這個文檔:http://www.ibm.com/developerworks/library/j-nativememory-linux/index.html – gouessej

2

在我看來,你不需要擔心釋放ByteBuffer pixels,因爲它是由JVM管理的。你應該真正關心的是在C++使用它時避免垃圾收集。