近期
也有一段时间没更新文章了,最近刚好在写一些jni相关的函数调用。JNI函数真的是太多了,一段时间不写很简略忘掉。
这儿比较推荐官方的手册,至少API很全Java官方JNI手册
GetFloatArrayElements 办法
当我想要获取一个jfloatArray 的内容时,一般咱们会调用以下JNI办法
jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
这个办法回来值是jfloat* ,一起承受三个参数,前两个参数比较好了解,一个是JNIEnv,是当时JNI环境的一个指针,一般咱们都可以经过JavaVM或许默许JNI函数中获取,还有一个就是jfloatArray,代表着我想要读取的数组,最后一个参数就比较有意思了,是否是复制
下面举一个比方
声明一个数组,在自界说类中
val test = FloatArray(10)
@JvmStatic
fun getFloatArray():FloatArray{
return test
}
C代码调用getFloatArray获取Kotlin数组
jclass clz = (*env)->GetObjectClass(env, thiz);
jmethodID id = (*env)->GetStaticMethodID(env, clz, "getFloatArray", "()[F");
jfloatArray floatArray = (*env)->CallStaticObjectMethod(env, clz, id);
jsize size = (*env)->GetArrayLength(env, floatArray);
float *native_array = (*env)->GetFloatArrayElements(env, floatArray, NULL);
for (int i = 0; i < size; ++i) {
__android_log_print(ANDROID_LOG_ERROR, "hello", "i is %f", native_array[i]);
}
(*env)->ReleaseFloatArrayElements(env, floatArray, native_array, 0);
从AOSP上可以看到,大部分使用到GetFloatArrayElements的时候,最后一个参数都是NULL,那么这个参数是怎么使用呢?
从Java官方JNI手册 咱们可以看到,当最后一个参数isCopy不为NULL时,isCopy会被设定为JNI_TRUE(常量1),反之是JNI_FALSE(0)
实际上,咱们依照上面比方多加一行打印承认,发现也确实是1
jboolean flag = 1;
float *native_array = (*env)->GetFloatArrayElements(env, floatArray, &flag);
__android_log_print(ANDROID_LOG_ERROR, "hello", "flag is %d", flag);
那么事情到这儿了,关于一个Java开发者来说,应该是结束了,但是关于Android开发者来说,故事还没那么简略。
这儿给大家提出一个小疑问:有没有情况,即便GetFloatArrayElements第三个参数isCopy传递为一个非NULL的值,会导致isCopy 为false呢?答案是有的,咱们把目光转到ART虚拟机上。
ART虚拟机针对JNI完成
上面咱们说到Java官方JNI手册,它算是一个协议,商定了Java虚拟机的开始协定,包括JNI,而在ART虚拟机中,针对JNI的完成在jni_internal.cc 中
这儿咱们看到GetPrimitiveArray在ART的完成
template <typename ArrayT, typename ElementT, typename ArtArrayT>
static ElementT* GetPrimitiveArray(JNIEnv* env, ArrayT java_array, jboolean* is_copy) {
CHECK_NON_NULL_ARGUMENT(java_array);
ScopedObjectAccess soa(env);
ObjPtr<ArtArrayT> array = DecodeAndCheckArrayType<ArrayT, ElementT, ArtArrayT>(
soa, java_array, "GetArrayElements", "get");
if (UNLIKELY(array == nullptr)) {
return nullptr;
}
注意这儿
if (Runtime::Current()->GetHeap()->IsMovableObject(array)) {
if (is_copy != nullptr) {
*is_copy = JNI_TRUE;
}
const size_t component_size = sizeof(ElementT);
size_t size = array->GetLength() * component_size;
void* data = new uint64_t[RoundUp(size, 8) / 8];
memcpy(data, array->GetData(), size);
return reinterpret_cast<ElementT*>(data);
} else {
咱们的答案出现了
if (is_copy != nullptr) {
*is_copy = JNI_FALSE;
}
return reinterpret_cast<ElementT*>(array->GetData());
}
}
这儿咱们就找到了刚刚的问题,即便is_copy不为NULL,也会为JNI_FALSE的情况。Runtime::Current()->GetHeap()->IsMovableObject(array),这儿有一个要害的函数,咱们知道ART有着自己的内存办理,不同于一般Java虚拟机,这儿咱们知道,假如分配的array不是一个Movable的目标,那么即便is_copy不为NULL,也会回来JNI_FALSE。
那么如何界说Movable目标呢?
其实终究会调用到FindContinuousSpaceFromAddress办法
space::ContinuousSpace* Heap::FindContinuousSpaceFromAddress(const mirror::Object* addr) const {
for (const auto& space : continuous_spaces_) {
if (space->Contains(addr)) {
return space;
}
}
return nullptr;
}
这个办法十分简略,就是看咱们分配的array指针在哪一块Space上,假如它在continuous_spaces_上,其实就会回来true!
Space区分
咱们在ART内存模型这一篇中说到,ART中存在着十分多的内存模型区分,比方
// All-known continuous spaces, where objects lie within fixed bounds.
std::vector<space::ContinuousSpace*> continuous_spaces_ GUARDED_BY(Locks::mutator_lock_);
// All-known discontinuous spaces, where objects may be placed throughout virtual memory.
std::vector<space::DiscontinuousSpace*> discontinuous_spaces_ GUARDED_BY(Locks::mutator_lock_);
// All-known alloc spaces, where objects may be or have been allocated.
std::vector<space::AllocSpace*> alloc_spaces_;
// A space where non-movable objects are allocated, when compaction is enabled it contains
// Classes, ArtMethods, ArtFields, and non moving objects.
space::MallocSpace* non_moving_space_;
// Space which we use for the kAllocatorTypeROSAlloc.
space::RosAllocSpace* rosalloc_space_;
// Space which we use for the kAllocatorTypeDlMalloc.
space::DlMallocSpace* dlmalloc_space_;
Space 归于哪一块,会在虚拟机Heap初始化时参加AddSpace
void Heap::AddSpace(space::Space* space) {
CHECK(space != nullptr);
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
条件
if (space->IsContinuousSpace()) {
DCHECK(!space->IsDiscontinuousSpace());
space::ContinuousSpace* continuous_space = space->AsContinuousSpace();
// Continuous spaces don't necessarily have bitmaps.
accounting::ContinuousSpaceBitmap* live_bitmap = continuous_space->GetLiveBitmap();
accounting::ContinuousSpaceBitmap* mark_bitmap = continuous_space->GetMarkBitmap();
// The region space bitmap is not added since VisitObjects visits the region space objects with
// special handling.
if (live_bitmap != nullptr && !space->IsRegionSpace()) {
CHECK(mark_bitmap != nullptr);
live_bitmap_->AddContinuousSpaceBitmap(live_bitmap);
mark_bitmap_->AddContinuousSpaceBitmap(mark_bitmap);
}
这儿就参加了上面看到的continuous_spaces_
continuous_spaces_.push_back(continuous_space);
// Ensure that spaces remain sorted in increasing order of start address.
std::sort(continuous_spaces_.begin(), continuous_spaces_.end(),
[](const space::ContinuousSpace* a, const space::ContinuousSpace* b) {
return a->Begin() < b->Begin();
});
} else {
CHECK(space->IsDiscontinuousSpace());
space::DiscontinuousSpace* discontinuous_space = space->AsDiscontinuousSpace();
live_bitmap_->AddLargeObjectBitmap(discontinuous_space->GetLiveBitmap());
mark_bitmap_->AddLargeObjectBitmap(discontinuous_space->GetMarkBitmap());
discontinuous_spaces_.push_back(discontinuous_space);
}
if (space->IsAllocSpace()) {
alloc_spaces_.push_back(space->AsAllocSpace());
}
}
它的条件是space->IsContinuousSpace()回来true
咱们再来看一下ART虚拟机内存模型,其中有一个很要害的笼统父类Space
Space
virtual bool IsContinuousSpace() const {
return false;
}
Space中默许办法是回来false,但是它的子类ContinueSpace会重写回来true
而咱们熟知的大目标LargeObjectSpace,其实仍是回来false!
让IsMovableObject回来false
回到GetPrimitiveArray办法,咱们就知道了,假如归于大目标,那么被分配到的LargeObjectSpace后,那么Runtime::Current()->GetHeap()->IsMovableObject(array) 就会回来false!大目标的界说咱们可以在Heap分配时知道,当分配的归于数组或许string大等于large_object_threshold_(默许12kb)时,就会被分配到LargeObjectSpace
art/runtime/gc/heap-inl.h
inline bool Heap::ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const {
// We need to have a zygote space or else our newly allocated large object can end up in the
// Zygote resulting in it being prematurely freed.
// We can only do this for primitive objects since large objects will not be within the card table
// range. This also means that we rely on SetClass not dirtying the object's card.
return byte_count >= large_object_threshold_ && (c->IsPrimitiveArray() || c->IsStringClass());
}
因而,咱们只需要把数组改大一些,就能让iscopy回来false
val test = FloatArray(1024*1024)
@JvmStatic
fun getFloatArray():FloatArray{
return test
}
修改后咱们再执行
jboolean flag = 1;
float *native_array = (*env)->GetFloatArrayElements(env, floatArray, &flag);
__android_log_print(ANDROID_LOG_ERROR, "hello", "flag is %d", flag);
达到了咱们验证的目的!
最后
本次试验初衷是让读者们知道,ART虚拟机中,其实会有一部分归于定制操作,区别于一般的Java虚拟机标准,咱们了解ART详细完成之后,能让咱们更加了解这些现象。
下次面试官再问到,当调用GetFloatArrayElements iscopy为非空时,回来一定是JNI_TRUE吗?相信你应该可以回答!