In effect, the Java side passed an array of integers to a native method, that, in turn, processed it as an array of bytes, returning it as an array of integers.
Later, I had to change the Java side so it actually used BufferedImages made up of bytes, not integers, so the code looked like this:
This also ran. But, it also crashed, and did so both erratically and enigmatically, with an error message something like this:
# An unexpected error has been detected by Java Runtime Environment:
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d830c3b, pid=2352, tid=2404
The problem turned out to be in Line 7 (and, in a way, also Line 11) of my native method. I was still using GetIntArrayElements() when I should have been using GetByteArrayElements(). C++, of course, is very relaxed about casting pointers from one type to another, so I was able to pass what had been a pointer to an array of integers to something expecting a pointer to an array of bytes, and make it work (indeed, a lot of perfectly good C/C++ code relies on the ability to treat integers as bytes in just this way). So, when GetIntArrayElements() continued to return a pointer to a memory buffer, it never occurred to me that there was anything wrong. But there was: the GetIntArrayElements() and GetByteArrayElements() methods either pin down the original array, or else copy it. Either way, they need to be able to copy back your data if your changes are going to show up on the Java side when your native method returns. Apparently, either method called on a jbyteArray will return a valid C++ pointer to its memory, but only GetByteArrayElements() correctly manages the business of returning your data. Calling GetIntArrayElements() gives you a valid pointer, but it doesn't protect the original object in the same way GetByteArrayElements() does, allowing the error above to come up from time to time.
What made this hard to discover was that both methods do return a valid pointer, not an error code. That's somewhat surprising, but maybe that was a design decision made in favor of efficiency over type safety. Or, maybe checking the type of the array passed to those methods is harder than I think it is. Regardless, it was an annoying bug, and hard to Google (since that error message can have any number of causes, most of which have little to do with what I was doing wrong).
I get a lot of good help here. Maybe this will be of benefit to someone else.
C++, of course, is very relaxed about casting pointers from one type to another
Just want to point out that this is false. What your code depicts is a C-style cast, which is a legacy artifact from C. Static pointer casting in C++ is completely type-safe when the language is not abused by c-style and reinterpret_casting used in improper contexts, though JNI is a C interface and not a C++ interface. Another reason to use extra caution when blending C and C++