Is there a way to make JNI memory persistent?

1,380 views
Skip to first unread message

Jim Graham

unread,
Aug 8, 2012, 11:56:21 AM8/8/12
to andro...@googlegroups.com
I'm trying to improve one NDK (and OpenCV---but this is not about the
OpenCV part) function: blending two images (one photo, one photographic
color filter), by adding the ability for the user to control the opacity
of the filter image. To do this, unless I've missed something, I need to
use a seekbar in java and keep calling this function as the seekbar is
moved by the user. With what I know now, that means repeatedly passing
the photo's byte array and the filter's bitmap specs, re-creating the
filter bitmap in C++, converting the photo to a Mat, and so on, which is
not exactly efficient in my opinion.

So, is there a way to have the Mat images that I'll be using for blending
remain in memory (and be usable) between going back to java, getting new
information, and then back to JNI? In other words, something like this:

a) user takes photo, byte array specs on filters in use, and default
alpha from seekbar sent to blend function (C++) in JNI;

b) byte array converted to Mat; filter specs used to create the
filter's Mat (or filters' Mat if more than one in use);

c) store the two Mats created in memory to refer to later when we
return to the JNI side

d) blend photo's Mat and Mat filter(s) using alpha passed to JNI

e) crete display-size image and pass back to java side for display

f) user adjusts alpha; repeat from step d using saved Mat data OR
user chooses to save result; save resulting (full-size) image
from JNI.

The question is, is step 'd' possible, and how would I do that?
An "RTFM" is perfectly acceptable, but please point me to the
appropriate "FM" first. :-)

Thanks,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) MiSTie #49997 < Running Mac OS X Lion >
spook...@gmail.com ICBM/Hurr.: 30.44406N 86.59909W

"The iPad is a status symbol for yuppies. The Android
is for people who actually want something that works."

Android Apps Listing at http://www.jstrack.org/barcodes.html

RichardC

unread,
Aug 8, 2012, 12:16:51 PM8/8/12
to andro...@googlegroups.com
Have a look at the JNI function NewDirectByteBuffer.  You can use it to allocate the memory for a ByteBuffer which you return to the Java side.  In the C/C++ side you keep hold of the the pointer to raw memory.

Or you can use GetDirectBufferAddress to get the address of the buffer backing a native ByteBuffer . See:

Jim Graham

unread,
Aug 8, 2012, 12:22:21 PM8/8/12
to andro...@googlegroups.com
On Wed, Aug 08, 2012 at 09:16:51AM -0700, RichardC wrote:
> Have a look at the JNI function NewDirectByteBuffer. You can use it to
> allocate the memory for a ByteBuffer which you return to the Java side. In
> the C/C++ side you keep hold of the the pointer to raw memory.
>
> Or you can use GetDirectBufferAddress to get the address of the buffer
> backing a native ByteBuffer . See:
> http://docs.oracle.com/javase/1.4.2/docs/guide/jni/jni-14.html

Thanks. I'll be pulling up that site after I send this ... and if I
still have questions, I'll be back with another followup. If not, I'll
say thanks now and move on. :-)

Thanks, either way,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) MiSTie #49997 < Running Mac OS X Lion >
spook...@gmail.com ICBM/Hurr.: 30.44406N 86.59909W

"Now what *you* need is a proper pint of porter poured in a proper
pewter porter pot.." --Peter Dalgaard in alt.sysadmin.recovery

Jim Graham

unread,
Aug 8, 2012, 12:51:10 PM8/8/12
to andro...@googlegroups.com
On Wed, Aug 08, 2012 at 09:16:51AM -0700, RichardC wrote:
> Have a look at the JNI function NewDirectByteBuffer. You can use it to
> allocate the memory for a ByteBuffer which you return to the Java side. In
> the C/C++ side you keep hold of the the pointer to raw memory.

Ok, I already have some questions. The link you pointed me to says two
things that immediately cause concern....

1) It mentions that an exception is thrown if JNI access to the buffer
is not supported by the device ... but that's the whole point---save
the Mat data for later reference from JNI.

2) It also mentions an OutOfMemory error.... Correct me if I'm wrong,
but isn't that error thrown when you exceed the arbitrary limits set
on the JAVA side? There should not BE any allocations on the Java
side...that's a 100% certain OOM if that were attempted.

Am I mis-reading this document? Does it touch the java side at all?
Or is the buffer allocated in JNI for read/write from JNI (which is what
I'm looking for)?

Thanks,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) MiSTie #49997 < Running Mac OS X Lion >
spook...@gmail.com ICBM/Hurr.: 30.44406N 86.59909W

"Now what *you* need is a proper pint of porter poured in a proper
pewter porter pot.." --Peter Dalgaard in alt.sysadmin.recovery

RichardC

unread,
Aug 8, 2012, 1:28:55 PM8/8/12
to andro...@googlegroups.com
>1) It mentions that an exception is thrown if JNI access to the buffer ...
Direct ByteBuffers are not supported on all Java VMs.  Lucky for us to get OpenGL to work at all we need them to work on the Android Platform.

>2) It also mentions an OutOfMemory error....
This could be thrown when creating the Java ByteBuffer Object (not its backing memory).  It's only a small object but Java could still be out of memory for any Object allocation.


I use this technique to decoding PNG files, in my own Java code, I do this to avoid the alpha pre-multiply done by the Android Bitmap class (it's not very OpenGL friendly).

I allocate the memory on the JNI side with code similar to:

JNIEXPORT extern jobject Java_long_package_name_jniAllocate(JNIEnv * pEnv, jclass pClass, jint capacity)
{
  void * buffer = new unsigned char[capacity];
  s_buffer = buffer;   // save the buffer location for later use
  jobject directBuffer = pEnv->NewDirectByteBuffer(buffer, capacity);
  return directBuffer;
}

Note the function returns a jobject which is a ByteBuffer (backed by native memory).

On the Java size I have a PNG decoder class that makes use of the returned byte buffer and decodes a PNG from assets.   As the contents of the native buffer are visible to both Java (via the ByteBuffer) and C/C++ (via s_buffer) there is no copying needed across Java/JNI calls.  This part of the program is single threaded so I don't worry about locking.

Note: if you take apart the Android Bitmap class you can see it using exactly the same technique as above.  You will need the AOSP sources to see the JNI part.

Jim Graham

unread,
Aug 8, 2012, 2:02:13 PM8/8/12
to andro...@googlegroups.com
On Wed, Aug 08, 2012 at 10:28:55AM -0700, RichardC wrote:
> >1) It mentions that an exception is thrown if JNI access to the buffer ...
> Direct ByteBuffers are not supported on all Java VMs. Lucky for us to get
> OpenGL to work at all we need them to work on the Android Platform.

So I CAN create a byte buffer from JNI for exclusive access from JNI
(NO access from Java will be used at all).

> >2) It also mentions an OutOfMemory error....
> This could be thrown when creating the Java ByteBuffer Object (not its
> backing memory). It's only a small object but Java could still be out of
> memory for any Object allocation.

The memory allocation on the Java side, when using even a lowly 5 MP
camera (e.g., on my tablet) is already extremely tight. I've already
been told that Android devices with higher resolution cameras have
larger limits on heap size, but I can't blindly assume that they're
any larger (relatively speaking) than my tablet, for memory vs MP.

So consider it safe to assume that it will be extremely tight in every
case where the user uses the camera's full resolution, and that
therefore, all remaining memory usage in Java should be severely
minimized until the (potentially) HUGE byte array can be released,
which is after this part I'm working on (or rather, will be working
on once the migraine I had yesterday, which has been screaming at
me today every time I even think about looking at code, warning that
it will return if I do, has gone away ... aftermath of cancer #1 again).

> I use this technique to decoding PNG files, in my own Java code, I do this

Keep in mind, I'm not doing any of this in Java---all JNI. I need to be
able to create a memory block (or use some other method other than saving
the files to disk---which I'm doing now---as this is way too slow) that
uses only JNI-side memory, for read/write access from JNI after briefly
returning to Java, getting the latest seekbar value, and then calling
the C++ function again with that value (for the alpha to use when
blending the two images).

Thanks,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) MiSTie #49997 < Running Mac OS X Lion >
spook...@gmail.com ICBM/Hurricane: 30.44406N 86.59909W

"Someone ever tries to kill you, you try to kill 'em right back!"
--Mal (Firefly, 1x03, Our Mrs. Reynolds)

Jim Graham

unread,
Aug 9, 2012, 7:29:18 AM8/9/12
to andro...@googlegroups.com
On Wed, Aug 08, 2012 at 10:28:55AM -0700, RichardC wrote:

> I allocate the memory on the JNI side with code similar to:
>
> JNIEXPORT extern jobject Java_long_package_name_jniAllocate(JNIEnv * pEnv,
> jclass pClass, jint capacity)
> {
> void * buffer = new unsigned char[capacity];
> s_buffer = buffer; // save the buffer location for later use
> jobject directBuffer = pEnv->NewDirectByteBuffer(buffer, capacity);
> return directBuffer;
> }
>
> Note the function returns a jobject which is a ByteBuffer (backed by native
> memory).

> On the Java size I have a PNG decoder class that makes use of the returned
> byte buffer and decodes a PNG from assets. As the contents of the native
> buffer are visible to both Java (via the ByteBuffer) and C/C++ (via
> s_buffer) there is no copying needed across Java/JNI calls. This part of
> the program is single threaded so I don't worry about locking.

I completely missed this part yesterday---my head was still foggy from
the migraine the day before.... So how do you place data into this
memory allocation, and how do you then access it, both from the JNI and
Java sides?

Thanks,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) MiSTie #49997 < Running Mac OS X Lion >
spook...@gmail.com ICBM/Hurricane: 30.44406N 86.59909W

Do not look into waveguide with remaining eye!

Jim Graham

unread,
Aug 11, 2012, 9:01:53 AM8/11/12
to andro...@googlegroups.com
On Thu, Aug 09, 2012 at 06:29:18AM -0500, Jim Graham wrote:
> On Wed, Aug 08, 2012 at 10:28:55AM -0700, RichardC wrote:

> > On the Java size I have a PNG decoder class that makes use of the returned
> > byte buffer and decodes a PNG from assets. As the contents of the native
> > buffer are visible to both Java (via the ByteBuffer) and C/C++ (via
> > s_buffer) there is no copying needed across Java/JNI calls. This part of
> > the program is single threaded so I don't worry about locking.
>
> I completely missed this part yesterday---my head was still foggy from
> the migraine the day before.... So how do you place data into this
> memory allocation, and how do you then access it, both from the JNI and
> Java sides?

Again...how do you place, say, a bitmap or byte array (or both?) into
this block of memory, and how do you access it from both JNI and Java?
I've found plenty of resources stating how to allocate it...but none on
how to read from/write to it.

Thanks,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) | 1) "Smoking habanero powder helps defeat that
< Running Mac OS X Lion > | off taste' quite nicely."
spook...@gmail.com | 2) "I figure a couple bong hits of [habanero]
ICBM/Hurr.: / 30.44406N | powder would defeat just about anything!"
| 86.59909W --seen in Chile-Heads list

RichardC

unread,
Aug 11, 2012, 9:56:42 AM8/11/12
to andro...@googlegroups.com


On Thursday, August 9, 2012 12:29:18 PM UTC+1, Jim Graham wrote:
On Wed, Aug 08, 2012 at 10:28:55AM -0700, RichardC wrote:

> I allocate the memory on the JNI side with code similar to:
>
> JNIEXPORT extern jobject Java_long_package_name_jniAllocate(JNIEnv * pEnv,
> jclass pClass, jint capacity)
> {
>   void * buffer = new unsigned char[capacity];
>   s_buffer = buffer;   // save the buffer location for later use
>   jobject directBuffer = pEnv->NewDirectByteBuffer(buffer, capacity);
>   return directBuffer;
> }

This function above does all you need:
It allocates a block of C/C++ memory 
    void * buffer = new unsigned char[capacity]; 

Saves the raw pointer for further use in C/C++
    s_buffer = buffer;  

Creates a ByteBuffer to wrap the allocated memory
    jobject directBuffer = pEnv->NewDirectByteBuffer(buffer, capacity);  

and returns a ByteBuffer  to Java
    return directBuffer;  

To allocated the memory on the Java side you would call:
   ByteBuffer myByteBuffer = jniAllocate (1000);

Now you have a ByteBuffer (myByteBuffer) on the Java side and a C/C++ pointer (s_buffer) on the native side both of which refer to the same memory location.

Jim Graham

unread,
Aug 11, 2012, 10:24:35 AM8/11/12
to andro...@googlegroups.com
On Sat, Aug 11, 2012 at 06:56:42AM -0700, RichardC wrote:
> On Thursday, August 9, 2012 12:29:18 PM UTC+1, Jim Graham wrote:
> > On Wed, Aug 08, 2012 at 10:28:55AM -0700, RichardC wrote:
> >
> > > JNIEXPORT extern jobject Java_long_package_name_jniAllocate(JNIEnv *
> > pEnv,
> > > jclass pClass, jint capacity)
> > > {
> > > void * buffer = new unsigned char[capacity];
> > > s_buffer = buffer; // save the buffer location for later use
> > > jobject directBuffer = pEnv->NewDirectByteBuffer(buffer, capacity);
> > > return directBuffer;
> > > }
> >
>
> This function above does all you need:
> It allocates a block of C/C++ memory
> void * buffer = new unsigned char[capacity];

> Now you have a ByteBuffer (myByteBuffer) on the Java side and a C/C++
> pointer (s_buffer) on the native side both of which refer to the same
> memory location.

So if, for example, I allocate this to cover both the memory used by a
byte array from the camera and a bitmap (e.g., color filter, etc.) of
the same size as the image size (assuming it's the maximum size the
device's camera supports), both will be written to this new memory
allocation? Or is there something I need to do to MAKE that happen?
That's the question I was asking....

Thanks,
--jim

--
THE SCORE: ME: 2 CANCER: 0
73 DE N5IAL (/4) MiSTie #49997 < Running Mac OS X Lion >
spook...@gmail.com ICBM/Hurr.: 30.44406N 86.59909W

Seen in alt.sysadmin.recovery: "Priceless; that's better than telling
him to use the Read Manual command with the Real Fast option."
Reply all
Reply to author
Forward
0 new messages