dalvik_system_VMRuntime.cc revision f9c612f2e657adb0764285256b228622e0789254
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <limits.h> 18 19#include "class_linker-inl.h" 20#include "common_throws.h" 21#include "debugger.h" 22#include "dex_file-inl.h" 23#include "gc/accounting/card_table-inl.h" 24#include "gc/allocator/dlmalloc.h" 25#include "gc/heap.h" 26#include "gc/space/dlmalloc_space.h" 27#include "intern_table.h" 28#include "jni_internal.h" 29#include "mirror/art_method-inl.h" 30#include "mirror/class-inl.h" 31#include "mirror/dex_cache-inl.h" 32#include "mirror/object-inl.h" 33#include "object_utils.h" 34#include "scoped_fast_native_object_access.h" 35#include "scoped_thread_state_change.h" 36#include "thread.h" 37#include "thread_list.h" 38#include "toStringArray.h" 39 40namespace art { 41 42static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) { 43 return Runtime::Current()->GetHeap()->GetTargetHeapUtilization(); 44} 45 46static void VMRuntime_nativeSetTargetHeapUtilization(JNIEnv*, jobject, jfloat target) { 47 Runtime::Current()->GetHeap()->SetTargetHeapUtilization(target); 48} 49 50static void VMRuntime_startJitCompilation(JNIEnv*, jobject) { 51} 52 53static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { 54} 55 56static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, 57 jint length) { 58 ScopedFastNativeObjectAccess soa(env); 59 if (UNLIKELY(length < 0)) { 60 ThrowNegativeArraySizeException(length); 61 return nullptr; 62 } 63 mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); 64 if (UNLIKELY(element_class == nullptr)) { 65 ThrowNullPointerException(NULL, "element class == null"); 66 return nullptr; 67 } 68 if (UNLIKELY(element_class->IsPrimitiveVoid())) { 69 ThrowIllegalArgumentException(NULL, "Can't allocate an array of void"); 70 return nullptr; 71 } 72 Runtime* runtime = Runtime::Current(); 73 mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); 74 if (UNLIKELY(array_class == nullptr)) { 75 return nullptr; 76 } 77 gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator(); 78 mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length, 79 array_class->GetComponentSize(), allocator); 80 return soa.AddLocalReference<jobject>(result); 81} 82 83static jobject VMRuntime_newUnpaddedArray(JNIEnv* env, jobject, jclass javaElementClass, 84 jint length) { 85 ScopedFastNativeObjectAccess soa(env); 86 if (UNLIKELY(length < 0)) { 87 ThrowNegativeArraySizeException(length); 88 return nullptr; 89 } 90 mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); 91 if (UNLIKELY(element_class == nullptr)) { 92 ThrowNullPointerException(NULL, "element class == null"); 93 return nullptr; 94 } 95 if (UNLIKELY(element_class->IsPrimitiveVoid())) { 96 ThrowIllegalArgumentException(NULL, "Can't allocate an array of void"); 97 return nullptr; 98 } 99 Runtime* runtime = Runtime::Current(); 100 mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); 101 if (UNLIKELY(array_class == nullptr)) { 102 return nullptr; 103 } 104 gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); 105 mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length, 106 array_class->GetComponentSize(), allocator, 107 true); 108 return soa.AddLocalReference<jobject>(result); 109} 110 111static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { 112 if (javaArray == NULL) { // Most likely allocation failed 113 return 0; 114 } 115 ScopedFastNativeObjectAccess soa(env); 116 mirror::Array* array = soa.Decode<mirror::Array*>(javaArray); 117 if (!array->IsArrayInstance()) { 118 ThrowIllegalArgumentException(NULL, "not an array"); 119 return 0; 120 } 121 if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { 122 ThrowRuntimeException("Trying to get address of movable array object"); 123 return 0; 124 } 125 return reinterpret_cast<uintptr_t>(array->GetRawData(array->GetClass()->GetComponentSize(), 0)); 126} 127 128static void VMRuntime_clearGrowthLimit(JNIEnv*, jobject) { 129 Runtime::Current()->GetHeap()->ClearGrowthLimit(); 130} 131 132static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) { 133 return Dbg::IsDebuggerActive(); 134} 135 136static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) { 137 return toStringArray(env, Runtime::Current()->GetProperties()); 138} 139 140// This is for backward compatibility with dalvik which returned the 141// meaningless "." when no boot classpath or classpath was 142// specified. Unfortunately, some tests were using java.class.path to 143// lookup relative file locations, so they are counting on this to be 144// ".", presumably some applications or libraries could have as well. 145static const char* DefaultToDot(const std::string& class_path) { 146 return class_path.empty() ? "." : class_path.c_str(); 147} 148 149static jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) { 150 return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetBootClassPathString())); 151} 152 153static jstring VMRuntime_classPath(JNIEnv* env, jobject) { 154 return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetClassPathString())); 155} 156 157static jstring VMRuntime_vmVersion(JNIEnv* env, jobject) { 158 return env->NewStringUTF(Runtime::Current()->GetVersion()); 159} 160 161static jstring VMRuntime_vmLibrary(JNIEnv* env, jobject) { 162 return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so"); 163} 164 165static void VMRuntime_setTargetSdkVersionNative(JNIEnv* env, jobject, jint targetSdkVersion) { 166 // This is the target SDK version of the app we're about to run. 167 // Note that targetSdkVersion may be CUR_DEVELOPMENT (10000). 168 // Note that targetSdkVersion may be 0, meaning "current". 169 if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) { 170 Runtime* runtime = Runtime::Current(); 171 JavaVMExt* vm = runtime->GetJavaVM(); 172 if (vm->check_jni) { 173 LOG(INFO) << "CheckJNI enabled: not enabling JNI app bug workarounds."; 174 } else { 175 LOG(INFO) << "Turning on JNI app bug workarounds for target SDK version " 176 << targetSdkVersion << "..."; 177 178 vm->work_around_app_jni_bugs = true; 179 } 180 } 181} 182 183static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) { 184 if (UNLIKELY(bytes < 0)) { 185 ScopedObjectAccess soa(env); 186 ThrowRuntimeException("allocation size negative %d", bytes); 187 return; 188 } 189 Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, bytes); 190} 191 192static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) { 193 if (UNLIKELY(bytes < 0)) { 194 ScopedObjectAccess soa(env); 195 ThrowRuntimeException("allocation size negative %d", bytes); 196 return; 197 } 198 Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes); 199} 200 201static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) { 202 Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state)); 203} 204 205static void VMRuntime_trimHeap(JNIEnv*, jobject) { 206 Runtime::Current()->GetHeap()->Trim(); 207} 208 209static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { 210 Thread* self = ThreadForEnv(env); 211 Runtime::Current()->GetHeap()->ConcurrentGC(self); 212} 213 214typedef std::map<std::string, mirror::String*> StringTable; 215 216static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg, 217 uint32_t /*thread_id*/, RootType /*root_type*/) 218 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 219 StringTable& table = *reinterpret_cast<StringTable*>(arg); 220 mirror::String* string = const_cast<mirror::Object*>(*root)->AsString(); 221 table[string->ToModifiedUtf8()] = string; 222} 223 224// Based on ClassLinker::ResolveString. 225static void PreloadDexCachesResolveString(SirtRef<mirror::DexCache>& dex_cache, 226 uint32_t string_idx, 227 StringTable& strings) 228 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 229 mirror::String* string = dex_cache->GetResolvedString(string_idx); 230 if (string != NULL) { 231 return; 232 } 233 const DexFile* dex_file = dex_cache->GetDexFile(); 234 const char* utf8 = dex_file->StringDataByIdx(string_idx); 235 string = strings[utf8]; 236 if (string == NULL) { 237 return; 238 } 239 // LOG(INFO) << "VMRuntime.preloadDexCaches resolved string=" << utf8; 240 dex_cache->SetResolvedString(string_idx, string); 241} 242 243// Based on ClassLinker::ResolveType. 244static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t type_idx) 245 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 246 mirror::Class* klass = dex_cache->GetResolvedType(type_idx); 247 if (klass != NULL) { 248 return; 249 } 250 const DexFile* dex_file = dex_cache->GetDexFile(); 251 const char* class_name = dex_file->StringByTypeIdx(type_idx); 252 ClassLinker* linker = Runtime::Current()->GetClassLinker(); 253 if (class_name[1] == '\0') { 254 klass = linker->FindPrimitiveClass(class_name[0]); 255 } else { 256 klass = linker->LookupClass(class_name, NULL); 257 } 258 if (klass == NULL) { 259 return; 260 } 261 // LOG(INFO) << "VMRuntime.preloadDexCaches resolved klass=" << class_name; 262 dex_cache->SetResolvedType(type_idx, klass); 263 // Skip uninitialized classes because filled static storage entry implies it is initialized. 264 if (!klass->IsInitialized()) { 265 // LOG(INFO) << "VMRuntime.preloadDexCaches uninitialized klass=" << class_name; 266 return; 267 } 268 // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name; 269} 270 271// Based on ClassLinker::ResolveField. 272static void PreloadDexCachesResolveField(SirtRef<mirror::DexCache>& dex_cache, 273 uint32_t field_idx, 274 bool is_static) 275 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 276 mirror::ArtField* field = dex_cache->GetResolvedField(field_idx); 277 if (field != NULL) { 278 return; 279 } 280 const DexFile* dex_file = dex_cache->GetDexFile(); 281 const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); 282 mirror::Class* klass = dex_cache->GetResolvedType(field_id.class_idx_); 283 if (klass == NULL) { 284 return; 285 } 286 if (is_static) { 287 field = klass->FindStaticField(dex_cache.get(), field_idx); 288 } else { 289 field = klass->FindInstanceField(dex_cache.get(), field_idx); 290 } 291 if (field == NULL) { 292 return; 293 } 294 // LOG(INFO) << "VMRuntime.preloadDexCaches resolved field " << PrettyField(field); 295 dex_cache->SetResolvedField(field_idx, field); 296} 297 298// Based on ClassLinker::ResolveMethod. 299static void PreloadDexCachesResolveMethod(SirtRef<mirror::DexCache>& dex_cache, 300 uint32_t method_idx, 301 InvokeType invoke_type) 302 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 303 mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx); 304 if (method != NULL) { 305 return; 306 } 307 const DexFile* dex_file = dex_cache->GetDexFile(); 308 const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); 309 mirror::Class* klass = dex_cache->GetResolvedType(method_id.class_idx_); 310 if (klass == NULL) { 311 return; 312 } 313 switch (invoke_type) { 314 case kDirect: 315 case kStatic: 316 method = klass->FindDirectMethod(dex_cache.get(), method_idx); 317 break; 318 case kInterface: 319 method = klass->FindInterfaceMethod(dex_cache.get(), method_idx); 320 break; 321 case kSuper: 322 case kVirtual: 323 method = klass->FindVirtualMethod(dex_cache.get(), method_idx); 324 break; 325 default: 326 LOG(FATAL) << "Unreachable - invocation type: " << invoke_type; 327 } 328 if (method == NULL) { 329 return; 330 } 331 // LOG(INFO) << "VMRuntime.preloadDexCaches resolved method " << PrettyMethod(method); 332 dex_cache->SetResolvedMethod(method_idx, method); 333} 334 335struct DexCacheStats { 336 uint32_t num_strings; 337 uint32_t num_types; 338 uint32_t num_fields; 339 uint32_t num_methods; 340 DexCacheStats() : num_strings(0), 341 num_types(0), 342 num_fields(0), 343 num_methods(0) {} 344}; 345 346static const bool kPreloadDexCachesEnabled = true; 347 348// Disabled because it takes a long time (extra half second) but 349// gives almost no benefit in terms of saving private dirty pages. 350static const bool kPreloadDexCachesStrings = false; 351 352static const bool kPreloadDexCachesTypes = true; 353static const bool kPreloadDexCachesFieldsAndMethods = true; 354 355static const bool kPreloadDexCachesCollectStats = true; 356 357static void PreloadDexCachesStatsTotal(DexCacheStats* total) { 358 if (!kPreloadDexCachesCollectStats) { 359 return; 360 } 361 362 ClassLinker* linker = Runtime::Current()->GetClassLinker(); 363 const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath(); 364 for (size_t i = 0; i< boot_class_path.size(); i++) { 365 const DexFile* dex_file = boot_class_path[i]; 366 CHECK(dex_file != NULL); 367 total->num_strings += dex_file->NumStringIds(); 368 total->num_fields += dex_file->NumFieldIds(); 369 total->num_methods += dex_file->NumMethodIds(); 370 total->num_types += dex_file->NumTypeIds(); 371 } 372} 373 374static void PreloadDexCachesStatsFilled(DexCacheStats* filled) 375 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 376 if (!kPreloadDexCachesCollectStats) { 377 return; 378 } 379 ClassLinker* linker = Runtime::Current()->GetClassLinker(); 380 const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath(); 381 for (size_t i = 0; i< boot_class_path.size(); i++) { 382 const DexFile* dex_file = boot_class_path[i]; 383 CHECK(dex_file != NULL); 384 mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); 385 for (size_t i = 0; i < dex_cache->NumStrings(); i++) { 386 mirror::String* string = dex_cache->GetResolvedString(i); 387 if (string != NULL) { 388 filled->num_strings++; 389 } 390 } 391 for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { 392 mirror::Class* klass = dex_cache->GetResolvedType(i); 393 if (klass != NULL) { 394 filled->num_types++; 395 } 396 } 397 for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { 398 mirror::ArtField* field = dex_cache->GetResolvedField(i); 399 if (field != NULL) { 400 filled->num_fields++; 401 } 402 } 403 for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { 404 mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i); 405 if (method != NULL) { 406 filled->num_methods++; 407 } 408 } 409 } 410} 411 412// TODO: http://b/11309598 This code was ported over based on the 413// Dalvik version. However, ART has similar code in other places such 414// as the CompilerDriver. This code could probably be refactored to 415// serve both uses. 416static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { 417 if (!kPreloadDexCachesEnabled) { 418 return; 419 } 420 421 ScopedObjectAccess soa(env); 422 423 DexCacheStats total; 424 DexCacheStats before; 425 if (kPreloadDexCachesCollectStats) { 426 LOG(INFO) << "VMRuntime.preloadDexCaches starting"; 427 PreloadDexCachesStatsTotal(&total); 428 PreloadDexCachesStatsFilled(&before); 429 } 430 431 Runtime* runtime = Runtime::Current(); 432 ClassLinker* linker = runtime->GetClassLinker(); 433 Thread* self = ThreadForEnv(env); 434 435 // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings 436 StringTable strings; 437 if (kPreloadDexCachesStrings) { 438 runtime->GetInternTable()->VisitRoots(PreloadDexCachesStringsCallback, &strings, false, false); 439 } 440 441 const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath(); 442 for (size_t i = 0; i< boot_class_path.size(); i++) { 443 const DexFile* dex_file = boot_class_path[i]; 444 CHECK(dex_file != NULL); 445 SirtRef<mirror::DexCache> dex_cache(self, linker->FindDexCache(*dex_file)); 446 447 if (kPreloadDexCachesStrings) { 448 for (size_t i = 0; i < dex_cache->NumStrings(); i++) { 449 PreloadDexCachesResolveString(dex_cache, i, strings); 450 } 451 } 452 453 if (kPreloadDexCachesTypes) { 454 for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { 455 PreloadDexCachesResolveType(dex_cache.get(), i); 456 } 457 } 458 459 if (kPreloadDexCachesFieldsAndMethods) { 460 for (size_t class_def_index = 0; 461 class_def_index < dex_file->NumClassDefs(); 462 class_def_index++) { 463 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); 464 const byte* class_data = dex_file->GetClassData(class_def); 465 if (class_data == NULL) { 466 continue; 467 } 468 ClassDataItemIterator it(*dex_file, class_data); 469 for (; it.HasNextStaticField(); it.Next()) { 470 uint32_t field_idx = it.GetMemberIndex(); 471 PreloadDexCachesResolveField(dex_cache, field_idx, true); 472 } 473 for (; it.HasNextInstanceField(); it.Next()) { 474 uint32_t field_idx = it.GetMemberIndex(); 475 PreloadDexCachesResolveField(dex_cache, field_idx, false); 476 } 477 for (; it.HasNextDirectMethod(); it.Next()) { 478 uint32_t method_idx = it.GetMemberIndex(); 479 InvokeType invoke_type = it.GetMethodInvokeType(class_def); 480 PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type); 481 } 482 for (; it.HasNextVirtualMethod(); it.Next()) { 483 uint32_t method_idx = it.GetMemberIndex(); 484 InvokeType invoke_type = it.GetMethodInvokeType(class_def); 485 PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type); 486 } 487 } 488 } 489 } 490 491 if (kPreloadDexCachesCollectStats) { 492 DexCacheStats after; 493 PreloadDexCachesStatsFilled(&after); 494 LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d", 495 total.num_strings, before.num_strings, after.num_strings); 496 LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches types total=%d before=%d after=%d", 497 total.num_types, before.num_types, after.num_types); 498 LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d", 499 total.num_fields, before.num_fields, after.num_fields); 500 LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d", 501 total.num_methods, before.num_methods, after.num_methods); 502 LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches finished"); 503 } 504} 505 506 507/* 508 * This is called by the framework when it knows the application directory and 509 * process name. We use this information to start up the sampling profiler for 510 * for ART. 511 */ 512static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring appDir, jstring procName) { 513 const char *appDirChars = env->GetStringUTFChars(appDir, NULL); 514 const char *procNameChars = env->GetStringUTFChars(procName, NULL); 515 std::string profileFile = std::string(appDirChars) + "/art-profile-" + std::string(procNameChars); 516 Runtime::Current()->StartProfiler(profileFile.c_str()); 517 env->ReleaseStringUTFChars(appDir, appDirChars); 518 env->ReleaseStringUTFChars(procName, procNameChars); 519} 520 521static JNINativeMethod gMethods[] = { 522 NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"), 523 NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), 524 NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), 525 NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"), 526 NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), 527 NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), 528 NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), 529 NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), 530 NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), 531 NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), 532 NATIVE_METHOD(VMRuntime, newUnpaddedArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), 533 NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"), 534 NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"), 535 NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"), 536 NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"), 537 NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"), 538 NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"), 539 NATIVE_METHOD(VMRuntime, trimHeap, "()V"), 540 NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), 541 NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), 542 NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), 543 NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;Ljava/lang/String;)V"), 544}; 545 546void register_dalvik_system_VMRuntime(JNIEnv* env) { 547 REGISTER_NATIVE_METHODS("dalvik/system/VMRuntime"); 548} 549 550} // namespace art 551