26 #include "JNIMemoryManager.h"
38 #ifdef VSJNI_MEMMANAGER_DEBUG
42 #include <com/avpkit/ferry/config.h>
50 #include "RefCounted.h"
53 VSJNI_malloc(jobject obj,
size_t requested_size);
55 VSJNI_free(
void * mem);
57 VSJNI_alignMemory(
void *);
59 VSJNI_unalignMemory(
void*);
60 static size_t VSJNI_ALIGNMENT_BOUNDARY = 16;
67 JAVA_STANDARD_HEAP = 0,
68 JAVA_DIRECT_BUFFERS = 1,
69 JAVA_DIRECT_BUFFERS_WITH_STANDARD_HEAP_NOTIFICATION = 2,
71 NATIVE_BUFFERS_WITH_STANDARD_HEAP_NOTIFICATION = 4,
74 static enum VSJNIMemoryModel sVSJNI_IsMirroringNativeMemoryInJVM =
75 #ifdef VSJNI_USE_JVM_FOR_MEMMANAGEMENT
97 void * retval = VSJNI_malloc(
static_cast<jobject
> (obj), requested_size);
111 static JavaVM* sCachedJVM = 0;
112 #ifdef VSJNI_USE_JVM_FOR_MEMMANAGEMENT
115 static jclass sByteBufferReferenceClass = 0;
116 static jmethodID sByteBufferAllocateDirectMethod = 0;
117 static jclass sJNIMemoryAllocatorClass = 0;
118 static jmethodID sJNIMemoryAllocatorMallocMethod = 0;
119 static jmethodID sJNIMemoryAllocatorFreeMethod = 0;
129 VSJNI_MemoryManagerInit(JavaVM* aJVM)
133 #ifdef VSJNI_USE_JVM_FOR_MEMMANAGEMENT
140 JNIEnv *env = VSJNI_getEnv();
142 throw std::runtime_error(
"could not find environment");
144 jclass cls = env->FindClass(
"java/nio/ByteBuffer");
146 throw std::runtime_error(
"could not find java.nio.ByteBuffer class");
148 sByteBufferReferenceClass = (jclass) env->NewWeakGlobalRef(cls);
149 if (!sByteBufferReferenceClass)
150 throw std::runtime_error(
"could not get weak reference for class");
152 sByteBufferAllocateDirectMethod = env->GetStaticMethodID(cls,
153 "allocateDirect",
"(I)Ljava/nio/ByteBuffer;");
154 if (!sByteBufferAllocateDirectMethod)
155 throw std::runtime_error(
156 "could not find allocateDirect(int) method in java.nio.ByteBuffer");
157 env->DeleteLocalRef(cls);
158 if (env->ExceptionCheck())
159 throw std::runtime_error(
"got exception in jni");
161 cls = env->FindClass(
"com/avpkit/ferry/JNIMemoryAllocator");
163 throw std::runtime_error(
164 "could not find com.avpkit.ferry.JNIMemoryAllocatorclass");
166 sJNIMemoryAllocatorClass = (jclass) env->NewWeakGlobalRef(cls);
167 if (!sJNIMemoryAllocatorClass)
168 throw std::runtime_error(
"could not get weak reference for class");
170 sJNIMemoryAllocatorMallocMethod = env->GetMethodID(cls,
"malloc",
"(I)[B");
171 if (!sJNIMemoryAllocatorMallocMethod)
172 throw std::runtime_error(
173 "could not find malloc(int) method in com.avpkit.ferry.JNIMemoryAllocator");
174 sJNIMemoryAllocatorFreeMethod = env->GetMethodID(cls,
"free",
"([B)V");
175 if (!sJNIMemoryAllocatorFreeMethod)
176 throw std::runtime_error(
177 "could not find free(byte[]) method in com.avpkit.ferry.JNIMemoryAllocator");
179 catch (std::exception e)
183 #ifdef VSJNI_MEMMANAGER_DEBUG
185 "got exception initializing jvm; using stdlib for memory allocation\n");
192 #ifdef VSJNI_USE_JVM_FOR_MEMMANAGEMENT
199 jint retval = sCachedJVM->GetEnv((
void**) (
void*) &env, JNI_VERSION_1_2);
200 if (retval == JNI_EDETACHED)
201 throw std::runtime_error(
"not attached to JVM");
203 if (retval == JNI_EVERSION)
204 throw std::runtime_error(
"Java v1.2 not supported");
210 struct VSJNI_AllocationHeader
226 enum VSJNIMemoryModel mModel;
230 VS_JNI_malloc_native(JNIEnv *env, jobject obj,
size_t requested_size,
236 if (notifyJavaHeap && env){
237 jbyteArray bytearray= 0;
241 bytearray =
static_cast<jbyteArray
> (env->CallObjectMethod(obj,
242 sJNIMemoryAllocatorMallocMethod, requested_size
243 +
sizeof(VSJNI_AllocationHeader) + VSJNI_ALIGNMENT_BOUNDARY));
248 bytearray = env->NewByteArray(requested_size
249 +
sizeof(VSJNI_AllocationHeader) + VSJNI_ALIGNMENT_BOUNDARY);
252 throw std::bad_alloc();
258 env->CallVoidMethod(obj,
259 sJNIMemoryAllocatorFreeMethod, bytearray);
260 if (env->ExceptionCheck()) {
261 env->DeleteLocalRef(bytearray);
262 throw std::runtime_error(
"got java exception");
265 env->DeleteLocalRef(bytearray);
266 if (env->ExceptionCheck())
267 throw std::bad_alloc();
271 buffer = requested_size > 0 ? malloc((
size_t) requested_size +
sizeof(VSJNI_AllocationHeader)
272 + VSJNI_ALIGNMENT_BOUNDARY) : 0;
273 VSJNI_AllocationHeader *header = (VSJNI_AllocationHeader*) buffer;
275 throw std::bad_alloc();
278 memset(header, 0,
sizeof(VSJNI_AllocationHeader));
279 header->mModel = NATIVE_BUFFERS;
281 retval = (
void*) ((
char*) header +
sizeof(VSJNI_AllocationHeader));
286 VS_JNI_malloc_javaDirectBufferBacked(JNIEnv *env, jobject obj,
287 size_t requested_size,
293 jlong size = requested_size +
sizeof(VSJNI_AllocationHeader)
294 + VSJNI_ALIGNMENT_BOUNDARY;
296 if (env->ExceptionCheck()) {
297 throw std::bad_alloc();
302 jbyteArray bytearray= 0;
306 bytearray =
static_cast<jbyteArray
> (env->CallObjectMethod(obj,
307 sJNIMemoryAllocatorMallocMethod, requested_size
308 +
sizeof(VSJNI_AllocationHeader) + VSJNI_ALIGNMENT_BOUNDARY));
313 bytearray = env->NewByteArray(requested_size
314 +
sizeof(VSJNI_AllocationHeader) + VSJNI_ALIGNMENT_BOUNDARY);
317 throw std::bad_alloc();
323 env->CallVoidMethod(obj,
324 sJNIMemoryAllocatorFreeMethod, bytearray);
325 if (env->ExceptionCheck()) {
326 env->DeleteLocalRef(bytearray);
327 throw std::runtime_error(
"got java exception");
330 env->DeleteLocalRef(bytearray);
331 if (env->ExceptionCheck())
332 throw std::bad_alloc();
334 jclass cls =
static_cast<jclass
>(env->NewLocalRef(sByteBufferReferenceClass));
337 throw std::bad_alloc();
339 jobject bytearray = env->CallStaticObjectMethod(sByteBufferReferenceClass,
340 sByteBufferAllocateDirectMethod, (jint)size);
341 env->DeleteLocalRef(cls);
345 if (!bytearray || env->ExceptionCheck()) {
346 throw std::bad_alloc();
349 jlong availableCapacity = env->GetDirectBufferCapacity(bytearray);
350 if (env->ExceptionCheck()) {
351 throw std::bad_alloc();
353 if (availableCapacity < size) {
354 throw std::bad_alloc();
357 buffer = env->GetDirectBufferAddress(bytearray);
358 if (!buffer || env->ExceptionCheck()) {
359 throw std::bad_alloc();
367 VSJNI_AllocationHeader* header = (VSJNI_AllocationHeader*) buffer;
368 memset(header, 0,
sizeof(VSJNI_AllocationHeader));
372 header->mRef =
static_cast<jobject
> (env->NewGlobalRef(bytearray));
373 if (!header->mRef || env->ExceptionCheck()) {
374 throw std::bad_alloc();
376 header->mModel = JAVA_DIRECT_BUFFERS;
380 env->DeleteLocalRef(bytearray);
381 if (env->ExceptionCheck()) {
382 env->DeleteLocalRef(header->mRef);
383 throw std::bad_alloc();
386 retval = (
void*) ((
char*) buffer +
sizeof(VSJNI_AllocationHeader));
391 VS_JNI_malloc_javaByteBacked(JNIEnv* env, jobject obj,
size_t requested_size)
396 if (env->ExceptionCheck())
397 throw std::bad_alloc();
399 jbyteArray bytearray = 0;
403 bytearray =
static_cast<jbyteArray
> (env->CallObjectMethod(obj,
404 sJNIMemoryAllocatorMallocMethod, requested_size
405 +
sizeof(VSJNI_AllocationHeader) + VSJNI_ALIGNMENT_BOUNDARY));
410 bytearray = env->NewByteArray(requested_size
411 +
sizeof(VSJNI_AllocationHeader) + VSJNI_ALIGNMENT_BOUNDARY);
419 throw std::bad_alloc();
420 if (env->ExceptionCheck())
421 throw std::bad_alloc();
425 buffer = (
void*) (env->GetByteArrayElements(bytearray, 0));
426 if (env->ExceptionCheck())
427 throw std::bad_alloc();
433 throw std::bad_alloc();
435 VSJNI_AllocationHeader* header = (VSJNI_AllocationHeader*) buffer;
436 memset(header, 0,
sizeof(VSJNI_AllocationHeader));
440 header->mRef = env->NewGlobalRef(bytearray);
442 throw std::bad_alloc();
443 header->mAllocator = 0;
446 header->mAllocator = env->NewGlobalRef(obj);
447 if (!header->mAllocator)
448 throw std::bad_alloc();
450 header->mModel = JAVA_STANDARD_HEAP;
454 env->DeleteLocalRef(bytearray);
455 if (env->ExceptionCheck())
456 throw std::bad_alloc();
459 retval = (
void*) ((
char*) buffer +
sizeof(VSJNI_AllocationHeader));
464 VSJNI_malloc(jobject obj,
size_t requested_size)
471 if ((
size_t) requested_size > INT_MAX - VSJNI_ALIGNMENT_BOUNDARY)
477 env = VSJNI_getEnv();
478 enum VSJNIMemoryModel model = sVSJNI_IsMirroringNativeMemoryInJVM;
480 model = NATIVE_BUFFERS;
483 case JAVA_STANDARD_HEAP:
484 retval = VS_JNI_malloc_javaByteBacked(env, obj, requested_size);
486 case JAVA_DIRECT_BUFFERS:
487 retval = VS_JNI_malloc_javaDirectBufferBacked(env, obj, requested_size,
490 case JAVA_DIRECT_BUFFERS_WITH_STANDARD_HEAP_NOTIFICATION:
491 retval = VS_JNI_malloc_javaDirectBufferBacked(env, obj, requested_size,
495 retval = VS_JNI_malloc_native(env, obj, requested_size,
false);
497 case NATIVE_BUFFERS_WITH_STANDARD_HEAP_NOTIFICATION:
498 retval = VS_JNI_malloc_native(env, obj, requested_size,
true);
501 throw std::bad_alloc();
504 #ifdef VSJNI_MEMMANAGER_DEBUG
505 fprintf (stderr,
"alloc: returned %p(%lld) size (%ld); model: %d\n",
508 (
size_t)requested_size,
515 return VSJNI_alignMemory(retval);
517 catch (std::bad_alloc & e)
519 #ifdef VSJNI_MEMMANAGER_DEBUG
520 fprintf (stderr,
"alloc: bad_alloc of size %ld\n",
521 (
size_t)requested_size);
535 VSJNI_free(
void * mem)
541 mem = VSJNI_unalignMemory(mem);
544 buffer = (
void*) ((
char*) mem -
sizeof(VSJNI_AllocationHeader));
547 VSJNI_AllocationHeader *header = (VSJNI_AllocationHeader*) buffer;
548 enum VSJNIMemoryModel model = header->mModel;
549 JNIEnv* env = VSJNI_getEnv();
552 case JAVA_STANDARD_HEAP:
557 if (env->ExceptionCheck())
558 throw std::runtime_error(
"got java exception");
560 if (header->mAllocator)
566 env->CallVoidMethod(header->mAllocator,
567 sJNIMemoryAllocatorFreeMethod, header->mRef);
568 if (env->ExceptionCheck())
569 throw std::runtime_error(
"got java exception");
570 env->DeleteGlobalRef(header->mAllocator);
571 if (env->ExceptionCheck())
572 throw std::runtime_error(
"got java exception");
579 jbyteArray array =
static_cast<jbyteArray
> (env->NewLocalRef(
581 if (env->ExceptionCheck())
582 throw std::runtime_error(
"got java exception");
584 throw std::runtime_error(
"got java exception");
588 env->DeleteGlobalRef(header->mRef);
590 if (env->ExceptionCheck())
591 throw std::runtime_error(
"got java exception");
594 env->ReleaseByteArrayElements(array, (jbyte*) buffer, JNI_ABORT);
595 if (env->ExceptionCheck())
596 throw std::runtime_error(
"got java exception");
601 env->DeleteLocalRef(array);
602 if (env->ExceptionCheck())
603 throw std::runtime_error(
"got java exception");
607 case JAVA_DIRECT_BUFFERS:
609 case JAVA_DIRECT_BUFFERS_WITH_STANDARD_HEAP_NOTIFICATION:
615 jobject ref = header->mRef;
618 env->DeleteGlobalRef(ref);
623 case NATIVE_BUFFERS_WITH_STANDARD_HEAP_NOTIFICATION:
629 fprintf(stderr,
"ERROR: Should never get here\n");
633 #ifdef VSJNI_MEMMANAGER_DEBUG
634 printf(
"free: orig %p (%lld) adjusted %p (%lld); model %d\n",
645 static void *VSJNI_malloc(jobject,
size_t requested_size)
650 (
sizeof(
size_t) == 4 && (
size_t)requested_size > INT_MAX - VSJNI_ALIGNMENT_BOUNDARY) ||
651 ((int64_t)requested_size > LLONG_MAX - VSJNI_ALIGNMENT_BOUNDARY))
656 retval = malloc(requested_size + VSJNI_ALIGNMENT_BOUNDARY);
660 return VSJNI_alignMemory(retval);
663 VSJNI_free(
void * mem)
668 mem = VSJNI_unalignMemory(mem);
674 VSJNI_alignMemory(
void* aInput)
676 void* retval = aInput;
678 size_t alignDiff = ((-(size_t) retval - 1) & (VSJNI_ALIGNMENT_BOUNDARY - 1)) + 1;
679 retval = (
char*) retval + alignDiff;
680 ((
char*) retval)[-1] = (char) alignDiff;
681 #ifdef VSJNI_MEMMANAGER_DEBUG
682 printf (
"align: orig(%p:%lld) new(%p:%lld) align(%d)\n",
683 aInput, (int64_t)aInput,
684 retval, (int64_t) retval,
698 VSJNI_unalignMemory(
void *aInput)
700 size_t alignDiff = ((
char*) aInput)[-1];
701 void * retval = (
void*) (((
char*) aInput) - alignDiff);
702 #ifdef VSJNI_MEMMANAGER_DEBUG
703 printf (
"unalign: orig(%p:%lld) new(%p:%lld) align(%d)\n",
704 aInput, (int64_t)aInput,
705 retval, (int64_t) retval,
717 #ifdef VSJNI_OVERRIDE_CPP_NEW_AND_DELETE
719 operator new (
size_t requested_size)
721 return VSJNI_malloc(0, requested_size);
725 operator delete (
void *mem)
735 VS_API_EXPORT
void JNICALL
736 Java_com_avpkit_ferry_JNIMemoryAllocator_setAllocator(JNIEnv *, jclass,
737 jlong aNativeObj, jobject aMemMgr)
745 VS_API_EXPORT jobject JNICALL
746 Java_com_avpkit_ferry_JNIMemoryAllocator_getAllocator(JNIEnv *env, jclass,
754 result = env->NewLocalRef(result);
758 VS_API_EXPORT jint JNICALL
759 Java_com_avpkit_ferry_FerryJNI_getMemoryModel(JNIEnv *, jclass)
761 return (jint) sVSJNI_IsMirroringNativeMemoryInJVM;
764 VS_API_EXPORT
void JNICALL
765 Java_com_avpkit_ferry_FerryJNI_setMemoryModel(JNIEnv *, jclass, jint value)
767 #ifdef VSJNI_USE_JVM_FOR_MEMMANAGEMENT
768 sVSJNI_IsMirroringNativeMemoryInJVM = (
enum VSJNIMemoryModel) value;
static void free(void *mem)
Free memory previously allocated by malloc(void*, size_t).
static void * malloc(size_t requested_size)
Create a malloced block of AT LEAST site_t bounds long.
Parent of all Ferry objects – it mains reference counts in native code.
void * getJavaAllocator()
This method is public but not part of the standard API.
void setJavaAllocator(void *allocator)
This method is public but not part of the standard API.
WARNING: Do not use logging in this class, and do not set any static file variables to values other t...