Here is a possible solution considering the small of information you gave.
On the Java side of things, you would have:
package com.stackoverflow;
public class JNIQuestion
{
static native void fillByteArray(byte[] buffer);
}
And on the C side you would have:
JNIEXPORT void JNICALL Java_com_stackoverflow_JNIQuestion_fillByteArray(JNIEnv* env, jbyteArray array)
{
jboolean isCopy;
jbyte* buffer = (*env)->GetByteArrayElements(env, array, &isCopy);
jsize length = (*env)->GetArrayLength(env, array);
jsize i;
// do something with the buffer here, replace with something meaningful
// PAY ATTENTION TO BUFFER OVERFLOW, DO NOT WRITE BEYOND BUFFER LENGTH
for (i = 0; i < length; ++i)
buffer[i] = i;
// here it is important to use 0 so that JNI takes care of copying
// the data back to the Java side in case GetByteArrayElements returned a copy
(*env)->ReleaseByteArrayElements(env, buffer, 0);
}
Using a direct ByteBuffer (ByteBuffer.allocateDirect()) is also a possible solution. However I only use a direct ByteBuffer when I need to fill data from the Java side a very precise offsets in the buffer.
About performance, the solution using a byte[] should be satisfactory as the JVM is likely to pin the byte array instead of copying it when invoking GetByteArrayElements().
Generally speaking, you will want to minimize the number of JNI calls which implies that accessing object fields from the C side or allocating Java from the C side will have an impact on performance.
In any case, profile first, optimize next.
PS: I didn't try to compile the code, there might be typos. Refer to the JNI Guide and the JNI Tutorial.