JNI 分析

本文围绕JNI方法调用及对IndirectReferenceTable操作展开分析,以7.2之前的源码为例,介绍了NDK开发时native调用java方法的实现,分析了JNIEnvExt::AddLocalReference、IndirectReferenceTable::Add等方法,还提及表的结构、初始化、最大值,以及何时remove等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JNI官网:

https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html

本文以7.2 之前的源码分析JNI方法调用及对IndirectReferenceTable操作。8.0代码有变动。

NDK开发时,native调用java方法是通过ENV获取函数指针数组,完成调用,env方法的实现在 jni_internal.cc中。

以 CallObjectMethod为例,


IndirectReferenceTable.add
ProfileSaver::FetchAndCacheResolvedClassesAndMethods(

1 JNI 方法调用
jni_internal.cc


static jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
676    va_list ap;
677    va_start(ap, mid);
678    CHECK_NON_NULL_ARGUMENT(obj);
679    CHECK_NON_NULL_ARGUMENT(mid);
680    ScopedObjectAccess soa(env);
681    JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
682    va_end(ap);
683    return soa.AddLocalReference<jobject>(result.GetL() //每次调用添加本地索引表
684  }


1.1 JNIEnvExt::AddLocalReference 

/art/runtime/jni_env_ext-inl.h

24namespace art {
25
26template<typename T>
27inline T JNIEnvExt::AddLocalReference(mirror::Object* obj) {
28  IndirectRef ref = locals.Add(local_ref_cookie, obj);//添加到表单
29
30  // TODO: fix this to understand PushLocalFrame, so we can turn it on.
31  if (false) {
32    if (check_jni) {
33      size_t entry_count = locals.Capacity();
34      if (entry_count > 16) {
35        locals.Dump(LOG(WARNING) << "Warning: more than 16 JNI local references: "
36            << entry_count << " (most recent was a " << PrettyTypeOf(obj) << ")\n");
37        // TODO: LOG(FATAL) in a later release?
38      }
39    }
40  }
41
42  return reinterpret_cast<T>(ref);
43}
44
45}
 
1.2 locals 是IndirectReferenceTable实例

/art/runtime/indirect_reference_table.cc

105IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) {
106  IRTSegmentState prevState;
107  prevState.all = cookie;
108  size_t topIndex = segment_state_.parts.topIndex;
109
110  CHECK(obj != nullptr);
111  VerifyObject(obj);
112  DCHECK(table_ != nullptr);
113  DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
114
115  if (topIndex == max_entries_) {
116    LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
117               << "(max=" << max_entries_ << ")\n"
118               << MutatorLockedDumpable<IndirectReferenceTable>(*this);
119  }
120
121  // We know there's enough room in the table.  Now we just need to find
122  // the right spot.  If there's a hole, find it and fill it; otherwise,
123  // add to the end of the list.
124  IndirectRef result;
125  int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
126  size_t index;
127  if (numHoles > 0) {
128    DCHECK_GT(topIndex, 1U);
129    // Find the first hole; likely to be near the end of the list.
130    IrtEntry* pScan = &table_[topIndex - 1];
131    DCHECK(!pScan->GetReference()->IsNull());
132    --pScan;
133    while (!pScan->GetReference()->IsNull()) {
134      DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
135      --pScan;
136    }
137    index = pScan - table_;
138    segment_state_.parts.numHoles--;
139  } else {
140    // Add to the end.
141    index = topIndex++;
142    segment_state_.parts.topIndex = topIndex;
143  }
144  table_[index].Add(obj); //添加到表单中
145  result = ToIndirectRef(index);
146  if ((false)) {
147    LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
148              << " holes=" << segment_state_.parts.numHoles;
149  }
150
151  DCHECK(result != nullptr);
152  return result;
153}

table_ 数据表的结构:

385  // Mem map where we store the indirect refs.
386  std::unique_ptr<MemMap> table_mem_map_;
387  // bottom of the stack. Do not directly access the object references
388  // in this as they are roots. Use Get() that has a read barrier.
389  IrtEntry* table_; 结构
390  /* bit mask, ORed into all irefs */
391  const IndirectRefKind kind_;
392  /* max #of entries allowed */
393  const size_t max_entries_;


表的初始化:

IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
71                                               size_t maxCount, IndirectRefKind desiredKind,
72                                               bool abort_on_error)
73    : kind_(desiredKind),
74      max_entries_(maxCount) {
75  CHECK_GT(initialCount, 0U);
76  CHECK_LE(initialCount, maxCount);
77  CHECK_NE(desiredKind, kHandleScopeOrInvalid);
78
79  std::string error_str;
80  const size_t table_bytes = maxCount * sizeof(IrtEntry);
81  table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
82                                            PROT_READ | PROT_WRITE, false, false, &error_str));
83  if (abort_on_error) {
84    CHECK(table_mem_map_.get() != nullptr) << error_str;
85    CHECK_EQ(table_mem_map_->Size(), table_bytes);
86    CHECK(table_mem_map_->Begin() != nullptr);
87  } else if (table_mem_map_.get() == nullptr ||
88             table_mem_map_->Size() != table_bytes ||
89             table_mem_map_->Begin() == nullptr) {
90    table_mem_map_.reset();
91    LOG(ERROR) << error_str;
92    return;
93  }
94  table_ = reinterpret_cast<IrtEntry*>(table_mem_map_->Begin());//初始化
95  segment_state_.all = IRT_FIRST_SEGMENT;
96}


定义处:/art/runtime/jni_env_ext.h
  // JNI local references.
67  IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
68

初始化:
6JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in)
57    : self(self_in),
58      vm(vm_in),
59      local_ref_cookie(IRT_FIRST_SEGMENT),
60      locals(kLocalsInitial, kLocalsMax, kLocal, false), //初始化
61      check_jni(false),
62      runtime_deleted(false),
63      critical(0),
64      monitors("monitors", kMonitorsInitial, kMonitorsMax) {
65  functions = unchecked_functions = GetJniNativeInterface();
66  if (vm->IsCheckJniEnabled()) {
67    SetCheckJniEnabled(true);
68  }
69}

static constexpr size_t kLocalsInitial = 64;  // Arbitrary.
最大值:
32// Maximum number of local references in the indirect reference table. The value is arbitrary but
33// low enough that it forces sanity checks.
34static constexpr size_t kLocalsMax = 512;


3 什么时候remove?
函数原型:


// Removes an object. We extract the table offset bits from "iref"
166// and zap the corresponding entry, leaving a hole if it's not at the top.
167// If the entry is not between the current top index and the bottom index
168// specified by the cookie, we don't remove anything. This is the behavior
169// required by JNI's DeleteLocalRef function.
170// This method is not called when a local frame is popped; this is only used
171// for explicit single removals.
172// Returns "false" if nothing was removed.
173bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) {
174  IRTSegmentState prevState;
175  prevState.all = cookie;
176  int topIndex = segment_state_.parts.topIndex;
177  int bottomIndex = prevState.parts.topIndex;
178
179  DCHECK(table_ != nullptr);
180  DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
181
182  if (GetIndirectRefKind(iref) == kHandleScopeOrInvalid) {
183    auto* self = Thread::Current();
184    if (self->HandleScopeContains(reinterpret_cast<jobject>(iref))) {
185      auto* env = self->GetJniEnv();
186      DCHECK(env != nullptr);
187      if (env->check_jni) {
188        ScopedObjectAccess soa(self);
189        LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
190        if (kDumpStackOnNonLocalReference) {
191          self->Dump(LOG(WARNING));
192        }
193      }
194      return true;
195    }
196  }


调用处:

void JNIEnvExt::DeleteLocalRef(jobject obj) {
87  if (obj != nullptr) {
88    locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
89  }
90}

只有在删除Local是才会清除表单:


B 全局索引表 
globals_

 // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
198  IndirectReferenceTable globals_;


 static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
519    ScopedObjectAccess soa(env);
520    mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
521    return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj);
522  }


初始化:

/art/runtime/java_vm_ext.cc

423JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options)
424    : runtime_(runtime),
425      check_jni_abort_hook_(nullptr),
426      check_jni_abort_hook_data_(nullptr),
427      check_jni_(false),  // Initialized properly in the constructor body below.
428      force_copy_(runtime_options.Exists(RuntimeArgumentMap::JniOptsForceCopy)),
429      tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
430                       || VLOG_IS_ON(third_party_jni)),
431      trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
432      globals_lock_("JNI global reference table lock"),
433      globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
434      libraries_(new Libraries),
435      unchecked_functions_(&gJniInvokeInterface),
436      weak_globals_lock_("JNI weak global reference table lock", kJniWeakGlobalsLock),
437      weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
438      allow_accessing_weak_globals_(true),
439      weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
440  functions = unchecked_functions_;
441  SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
442}

最大值:
45static size_t gGlobalsInitial = 512;  // Arbitrary.
46static size_t gGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值