1 /* 2 * Copyright (C) 2016 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 "cha.h" 18 19 #include "art_method-inl.h" 20 #include "jit/jit.h" 21 #include "jit/jit_code_cache.h" 22 #include "linear_alloc.h" 23 #include "runtime.h" 24 #include "scoped_thread_state_change-inl.h" 25 #include "stack.h" 26 #include "thread.h" 27 #include "thread_list.h" 28 #include "thread_pool.h" 29 30 namespace art { 31 32 void ClassHierarchyAnalysis::AddDependency(ArtMethod* method, 33 ArtMethod* dependent_method, 34 OatQuickMethodHeader* dependent_header) { 35 const auto it = cha_dependency_map_.insert( 36 decltype(cha_dependency_map_)::value_type(method, ListOfDependentPairs())).first; 37 it->second.push_back({dependent_method, dependent_header}); 38 } 39 40 static const ClassHierarchyAnalysis::ListOfDependentPairs s_empty_vector; 41 42 const ClassHierarchyAnalysis::ListOfDependentPairs& ClassHierarchyAnalysis::GetDependents( 43 ArtMethod* method) { 44 auto it = cha_dependency_map_.find(method); 45 if (it != cha_dependency_map_.end()) { 46 return it->second; 47 } 48 return s_empty_vector; 49 } 50 51 void ClassHierarchyAnalysis::RemoveAllDependenciesFor(ArtMethod* method) { 52 cha_dependency_map_.erase(method); 53 } 54 55 void ClassHierarchyAnalysis::RemoveDependentsWithMethodHeaders( 56 const std::unordered_set<OatQuickMethodHeader*>& method_headers) { 57 // Iterate through all entries in the dependency map and remove any entry that 58 // contains one of those in method_headers. 59 for (auto map_it = cha_dependency_map_.begin(); map_it != cha_dependency_map_.end(); ) { 60 ListOfDependentPairs& dependents = map_it->second; 61 dependents.erase( 62 std::remove_if( 63 dependents.begin(), 64 dependents.end(), 65 [&method_headers](MethodAndMethodHeaderPair& dependent) { 66 return method_headers.find(dependent.second) != method_headers.end(); 67 }), 68 dependents.end()); 69 70 // Remove the map entry if there are no more dependents. 71 if (dependents.empty()) { 72 map_it = cha_dependency_map_.erase(map_it); 73 } else { 74 map_it++; 75 } 76 } 77 } 78 79 // This stack visitor walks the stack and for compiled code with certain method 80 // headers, sets the should_deoptimize flag on stack to 1. 81 // TODO: also set the register value to 1 when should_deoptimize is allocated in 82 // a register. 83 class CHAStackVisitor FINAL : public StackVisitor { 84 public: 85 CHAStackVisitor(Thread* thread_in, 86 Context* context, 87 const std::unordered_set<OatQuickMethodHeader*>& method_headers) 88 : StackVisitor(thread_in, context, StackVisitor::StackWalkKind::kSkipInlinedFrames), 89 method_headers_(method_headers) { 90 } 91 92 bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { 93 ArtMethod* method = GetMethod(); 94 // Avoid types of methods that do not have an oat quick method header. 95 if (method == nullptr || 96 method->IsRuntimeMethod() || 97 method->IsNative() || 98 method->IsProxyMethod()) { 99 return true; 100 } 101 if (GetCurrentQuickFrame() == nullptr) { 102 // Not compiled code. 103 return true; 104 } 105 // Method may have multiple versions of compiled code. Check 106 // the method header to see if it has should_deoptimize flag. 107 const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); 108 DCHECK(method_header != nullptr); 109 if (!method_header->HasShouldDeoptimizeFlag()) { 110 // This compiled version doesn't have should_deoptimize flag. Skip. 111 return true; 112 } 113 auto it = std::find(method_headers_.begin(), method_headers_.end(), method_header); 114 if (it == method_headers_.end()) { 115 // Not in the list of method headers that should be deoptimized. 116 return true; 117 } 118 119 // The compiled code on stack is not valid anymore. Need to deoptimize. 120 SetShouldDeoptimizeFlag(); 121 122 return true; 123 } 124 125 private: 126 void SetShouldDeoptimizeFlag() REQUIRES_SHARED(Locks::mutator_lock_) { 127 QuickMethodFrameInfo frame_info = GetCurrentQuickFrameInfo(); 128 size_t frame_size = frame_info.FrameSizeInBytes(); 129 uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()); 130 size_t core_spill_size = POPCOUNT(frame_info.CoreSpillMask()) * 131 GetBytesPerGprSpillLocation(kRuntimeISA); 132 size_t fpu_spill_size = POPCOUNT(frame_info.FpSpillMask()) * 133 GetBytesPerFprSpillLocation(kRuntimeISA); 134 size_t offset = frame_size - core_spill_size - fpu_spill_size - kShouldDeoptimizeFlagSize; 135 uint8_t* should_deoptimize_addr = sp + offset; 136 // Set deoptimization flag to 1. 137 DCHECK(*should_deoptimize_addr == 0 || *should_deoptimize_addr == 1); 138 *should_deoptimize_addr = 1; 139 } 140 141 // Set of method headers for compiled code that should be deoptimized. 142 const std::unordered_set<OatQuickMethodHeader*>& method_headers_; 143 144 DISALLOW_COPY_AND_ASSIGN(CHAStackVisitor); 145 }; 146 147 class CHACheckpoint FINAL : public Closure { 148 public: 149 explicit CHACheckpoint(const std::unordered_set<OatQuickMethodHeader*>& method_headers) 150 : barrier_(0), 151 method_headers_(method_headers) {} 152 153 void Run(Thread* thread) OVERRIDE { 154 // Note thread and self may not be equal if thread was already suspended at 155 // the point of the request. 156 Thread* self = Thread::Current(); 157 ScopedObjectAccess soa(self); 158 CHAStackVisitor visitor(thread, nullptr, method_headers_); 159 visitor.WalkStack(); 160 barrier_.Pass(self); 161 } 162 163 void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) { 164 Thread* self = Thread::Current(); 165 ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); 166 barrier_.Increment(self, threads_running_checkpoint); 167 } 168 169 private: 170 // The barrier to be passed through and for the requestor to wait upon. 171 Barrier barrier_; 172 // List of method headers for invalidated compiled code. 173 const std::unordered_set<OatQuickMethodHeader*>& method_headers_; 174 175 DISALLOW_COPY_AND_ASSIGN(CHACheckpoint); 176 }; 177 178 void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class, 179 uint16_t verify_index, 180 ArtMethod* excluded_method) { 181 // Grab cha_lock_ to make sure all single-implementation updates are seen. 182 PointerSize image_pointer_size = 183 Runtime::Current()->GetClassLinker()->GetImagePointerSize(); 184 MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); 185 while (verify_class != nullptr) { 186 if (verify_index >= verify_class->GetVTableLength()) { 187 return; 188 } 189 ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size); 190 if (verify_method != excluded_method) { 191 DCHECK(!verify_method->HasSingleImplementation()) 192 << "class: " << verify_class->PrettyClass() 193 << " verify_method: " << verify_method->PrettyMethod(true) 194 << " excluded_method: " << excluded_method->PrettyMethod(true); 195 if (verify_method->IsAbstract()) { 196 DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr); 197 } 198 } 199 verify_class = verify_class->GetSuperClass(); 200 } 201 } 202 203 void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( 204 Handle<mirror::Class> klass, 205 ArtMethod* virtual_method, 206 ArtMethod* method_in_super, 207 std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, 208 PointerSize pointer_size) { 209 // TODO: if klass is not instantiable, virtual_method isn't invocable yet so 210 // even if it overrides, it doesn't invalidate single-implementation 211 // assumption. 212 213 DCHECK((virtual_method != method_in_super) || virtual_method->IsAbstract()); 214 DCHECK(method_in_super->GetDeclaringClass()->IsResolved()) << "class isn't resolved"; 215 // If virtual_method doesn't come from a default interface method, it should 216 // be supplied by klass. 217 DCHECK(virtual_method == method_in_super || 218 virtual_method->IsCopied() || 219 virtual_method->GetDeclaringClass() == klass.Get()); 220 221 // To make updating single-implementation flags simple, we always maintain the following 222 // invariant: 223 // Say all virtual methods in the same vtable slot, starting from the bottom child class 224 // to super classes, is a sequence of unique methods m3, m2, m1, ... (after removing duplicate 225 // methods for inherited methods). 226 // For example for the following class hierarchy, 227 // class A { void m() { ... } } 228 // class B extends A { void m() { ... } } 229 // class C extends B {} 230 // class D extends C { void m() { ... } } 231 // the sequence is D.m(), B.m(), A.m(). 232 // The single-implementation status for that sequence of methods begin with one or two true's, 233 // then become all falses. The only case where two true's are possible is for one abstract 234 // method m and one non-abstract method mImpl that overrides method m. 235 // With the invariant, when linking in a new class, we only need to at most update one or 236 // two methods in the sequence for their single-implementation status, in order to maintain 237 // the invariant. 238 239 if (!method_in_super->HasSingleImplementation()) { 240 // method_in_super already has multiple implementations. All methods in the 241 // same vtable slots in its super classes should have 242 // non-single-implementation already. 243 if (kIsDebugBuild) { 244 VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(), 245 method_in_super->GetMethodIndex(), 246 nullptr /* excluded_method */); 247 } 248 return; 249 } 250 251 uint16_t method_index = method_in_super->GetMethodIndex(); 252 if (method_in_super->IsAbstract()) { 253 if (kIsDebugBuild) { 254 // An abstract method should have made all methods in the same vtable 255 // slot above it in the class hierarchy having non-single-implementation. 256 mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); 257 VerifyNonSingleImplementation(super_super, 258 method_index, 259 method_in_super); 260 } 261 262 if (virtual_method->IsAbstract()) { 263 // SUPER: abstract, VIRTUAL: abstract. 264 if (method_in_super == virtual_method) { 265 DCHECK(klass->IsInstantiable()); 266 // An instantiable subclass hasn't provided a concrete implementation of 267 // the abstract method. Invoking method_in_super may throw AbstractMethodError. 268 // This is an uncommon case, so we simply treat method_in_super as not 269 // having single-implementation. 270 invalidated_single_impl_methods.insert(method_in_super); 271 return; 272 } else { 273 // One abstract method overrides another abstract method. This is an uncommon 274 // case. We simply treat method_in_super as not having single-implementation. 275 invalidated_single_impl_methods.insert(method_in_super); 276 return; 277 } 278 } else { 279 // SUPER: abstract, VIRTUAL: non-abstract. 280 // A non-abstract method overrides an abstract method. 281 if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { 282 // Abstract method_in_super has no implementation yet. 283 // We need to grab cha_lock_ since there may be multiple class linking 284 // going on that can check/modify the single-implementation flag/method 285 // of method_in_super. 286 MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); 287 if (!method_in_super->HasSingleImplementation()) { 288 return; 289 } 290 if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { 291 // virtual_method becomes the first implementation for method_in_super. 292 method_in_super->SetSingleImplementation(virtual_method, pointer_size); 293 // Keep method_in_super's single-implementation status. 294 return; 295 } 296 // Fall through to invalidate method_in_super's single-implementation status. 297 } 298 // Abstract method_in_super already got one implementation. 299 // Invalidate method_in_super's single-implementation status. 300 invalidated_single_impl_methods.insert(method_in_super); 301 return; 302 } 303 } else { 304 if (virtual_method->IsAbstract()) { 305 // SUPER: non-abstract, VIRTUAL: abstract. 306 // An abstract method overrides a non-abstract method. This is an uncommon 307 // case, we simply treat both methods as not having single-implementation. 308 invalidated_single_impl_methods.insert(virtual_method); 309 // Fall-through to handle invalidating method_in_super of its 310 // single-implementation status. 311 } 312 313 // SUPER: non-abstract, VIRTUAL: non-abstract/abstract(fall-through from previous if). 314 // Invalidate method_in_super's single-implementation status. 315 invalidated_single_impl_methods.insert(method_in_super); 316 317 // method_in_super might be the single-implementation of another abstract method, 318 // which should be also invalidated of its single-implementation status. 319 mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); 320 while (super_super != nullptr && 321 method_index < super_super->GetVTableLength()) { 322 ArtMethod* method_in_super_super = super_super->GetVTableEntry(method_index, pointer_size); 323 if (method_in_super_super != method_in_super) { 324 if (method_in_super_super->IsAbstract()) { 325 if (method_in_super_super->HasSingleImplementation()) { 326 // Invalidate method_in_super's single-implementation status. 327 invalidated_single_impl_methods.insert(method_in_super_super); 328 // No need to further traverse up the class hierarchy since if there 329 // are cases that one abstract method overrides another method, we 330 // should have made that method having non-single-implementation already. 331 } else { 332 // method_in_super_super is already non-single-implementation. 333 // No need to further traverse up the class hierarchy. 334 } 335 } else { 336 DCHECK(!method_in_super_super->HasSingleImplementation()); 337 // No need to further traverse up the class hierarchy since two non-abstract 338 // methods (method_in_super and method_in_super_super) should have set all 339 // other methods (abstract or not) in the vtable slot to be non-single-implementation. 340 } 341 342 if (kIsDebugBuild) { 343 VerifyNonSingleImplementation(super_super->GetSuperClass(), 344 method_index, 345 method_in_super_super); 346 } 347 // No need to go any further. 348 return; 349 } else { 350 super_super = super_super->GetSuperClass(); 351 } 352 } 353 } 354 } 355 356 void ClassHierarchyAnalysis::CheckInterfaceMethodSingleImplementationInfo( 357 Handle<mirror::Class> klass, 358 ArtMethod* interface_method, 359 ArtMethod* implementation_method, 360 std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, 361 PointerSize pointer_size) { 362 DCHECK(klass->IsInstantiable()); 363 DCHECK(interface_method->IsAbstract() || interface_method->IsDefault()); 364 365 if (!interface_method->HasSingleImplementation()) { 366 return; 367 } 368 369 if (implementation_method->IsAbstract()) { 370 // An instantiable class doesn't supply an implementation for 371 // interface_method. Invoking the interface method on the class will throw 372 // AbstractMethodError. This is an uncommon case, so we simply treat 373 // interface_method as not having single-implementation. 374 invalidated_single_impl_methods.insert(interface_method); 375 return; 376 } 377 378 // We need to grab cha_lock_ since there may be multiple class linking going 379 // on that can check/modify the single-implementation flag/method of 380 // interface_method. 381 MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); 382 // Do this check again after we grab cha_lock_. 383 if (!interface_method->HasSingleImplementation()) { 384 return; 385 } 386 387 ArtMethod* single_impl = interface_method->GetSingleImplementation(pointer_size); 388 if (single_impl == nullptr) { 389 // implementation_method becomes the first implementation for 390 // interface_method. 391 interface_method->SetSingleImplementation(implementation_method, pointer_size); 392 // Keep interface_method's single-implementation status. 393 return; 394 } 395 DCHECK(!single_impl->IsAbstract()); 396 if (single_impl->GetDeclaringClass() == implementation_method->GetDeclaringClass()) { 397 // Same implementation. Since implementation_method may be a copy of a default 398 // method, we need to check the declaring class for equality. 399 return; 400 } 401 // Another implementation for interface_method. 402 invalidated_single_impl_methods.insert(interface_method); 403 } 404 405 void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass, 406 ArtMethod* method, 407 PointerSize pointer_size) { 408 DCHECK(method->IsCopied() || method->GetDeclaringClass() == klass.Get()); 409 if (klass->IsFinal() || method->IsFinal()) { 410 // Final classes or methods do not need CHA for devirtualization. 411 // This frees up modifier bits for intrinsics which currently are only 412 // used for static methods or methods of final classes. 413 return; 414 } 415 if (method->IsAbstract()) { 416 // single-implementation of abstract method shares the same field 417 // that's used for JNI function of native method. It's fine since a method 418 // cannot be both abstract and native. 419 DCHECK(!method->IsNative()) << "Abstract method cannot be native"; 420 421 if (method->GetDeclaringClass()->IsInstantiable()) { 422 // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali). 423 // Do not attempt to devirtualize it. 424 method->SetHasSingleImplementation(false); 425 DCHECK(method->GetSingleImplementation(pointer_size) == nullptr); 426 } else { 427 // Abstract method starts with single-implementation flag set and null 428 // implementation method. 429 method->SetHasSingleImplementation(true); 430 DCHECK(method->GetSingleImplementation(pointer_size) == nullptr); 431 } 432 } else { 433 method->SetHasSingleImplementation(true); 434 // Single implementation of non-abstract method is itself. 435 DCHECK_EQ(method->GetSingleImplementation(pointer_size), method); 436 } 437 } 438 439 void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { 440 PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); 441 if (klass->IsInterface()) { 442 for (ArtMethod& method : klass->GetDeclaredVirtualMethods(image_pointer_size)) { 443 DCHECK(method.IsAbstract() || method.IsDefault()); 444 InitSingleImplementationFlag(klass, &method, image_pointer_size); 445 } 446 return; 447 } 448 449 mirror::Class* super_class = klass->GetSuperClass(); 450 if (super_class == nullptr) { 451 return; 452 } 453 454 // Keeps track of all methods whose single-implementation assumption 455 // is invalidated by linking `klass`. 456 std::unordered_set<ArtMethod*> invalidated_single_impl_methods; 457 458 // Do an entry-by-entry comparison of vtable contents with super's vtable. 459 for (int32_t i = 0; i < super_class->GetVTableLength(); ++i) { 460 ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); 461 ArtMethod* method_in_super = super_class->GetVTableEntry(i, image_pointer_size); 462 if (method == method_in_super) { 463 // vtable slot entry is inherited from super class. 464 if (method->IsAbstract() && klass->IsInstantiable()) { 465 // An instantiable class that inherits an abstract method is treated as 466 // supplying an implementation that throws AbstractMethodError. 467 CheckVirtualMethodSingleImplementationInfo(klass, 468 method, 469 method_in_super, 470 invalidated_single_impl_methods, 471 image_pointer_size); 472 } 473 continue; 474 } 475 InitSingleImplementationFlag(klass, method, image_pointer_size); 476 CheckVirtualMethodSingleImplementationInfo(klass, 477 method, 478 method_in_super, 479 invalidated_single_impl_methods, 480 image_pointer_size); 481 } 482 // For new virtual methods that don't override. 483 for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) { 484 ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); 485 InitSingleImplementationFlag(klass, method, image_pointer_size); 486 } 487 488 if (klass->IsInstantiable()) { 489 auto* iftable = klass->GetIfTable(); 490 const size_t ifcount = klass->GetIfTableCount(); 491 for (size_t i = 0; i < ifcount; ++i) { 492 mirror::Class* interface = iftable->GetInterface(i); 493 for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { 494 ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size); 495 mirror::PointerArray* method_array = iftable->GetMethodArray(i); 496 ArtMethod* implementation_method = 497 method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size); 498 DCHECK(implementation_method != nullptr) << klass->PrettyClass(); 499 CheckInterfaceMethodSingleImplementationInfo(klass, 500 interface_method, 501 implementation_method, 502 invalidated_single_impl_methods, 503 image_pointer_size); 504 } 505 } 506 } 507 508 InvalidateSingleImplementationMethods(invalidated_single_impl_methods); 509 } 510 511 void ClassHierarchyAnalysis::InvalidateSingleImplementationMethods( 512 std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) { 513 if (!invalidated_single_impl_methods.empty()) { 514 Runtime* const runtime = Runtime::Current(); 515 Thread *self = Thread::Current(); 516 // Method headers for compiled code to be invalidated. 517 std::unordered_set<OatQuickMethodHeader*> dependent_method_headers; 518 PointerSize image_pointer_size = 519 Runtime::Current()->GetClassLinker()->GetImagePointerSize(); 520 521 { 522 // We do this under cha_lock_. Committing code also grabs this lock to 523 // make sure the code is only committed when all single-implementation 524 // assumptions are still true. 525 MutexLock cha_mu(self, *Locks::cha_lock_); 526 // Invalidate compiled methods that assume some virtual calls have only 527 // single implementations. 528 for (ArtMethod* invalidated : invalidated_single_impl_methods) { 529 if (!invalidated->HasSingleImplementation()) { 530 // It might have been invalidated already when other class linking is 531 // going on. 532 continue; 533 } 534 invalidated->SetHasSingleImplementation(false); 535 if (invalidated->IsAbstract()) { 536 // Clear the single implementation method. 537 invalidated->SetSingleImplementation(nullptr, image_pointer_size); 538 } 539 540 if (runtime->IsAotCompiler()) { 541 // No need to invalidate any compiled code as the AotCompiler doesn't 542 // run any code. 543 continue; 544 } 545 546 // Invalidate all dependents. 547 for (const auto& dependent : GetDependents(invalidated)) { 548 ArtMethod* method = dependent.first;; 549 OatQuickMethodHeader* method_header = dependent.second; 550 VLOG(class_linker) << "CHA invalidated compiled code for " << method->PrettyMethod(); 551 DCHECK(runtime->UseJitCompilation()); 552 runtime->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor( 553 method, method_header); 554 dependent_method_headers.insert(method_header); 555 } 556 RemoveAllDependenciesFor(invalidated); 557 } 558 } 559 560 if (dependent_method_headers.empty()) { 561 return; 562 } 563 // Deoptimze compiled code on stack that should have been invalidated. 564 CHACheckpoint checkpoint(dependent_method_headers); 565 size_t threads_running_checkpoint = runtime->GetThreadList()->RunCheckpoint(&checkpoint); 566 if (threads_running_checkpoint != 0) { 567 checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint); 568 } 569 } 570 } 571 572 void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc) { 573 MutexLock mu(Thread::Current(), *Locks::cha_lock_); 574 for (auto it = cha_dependency_map_.begin(); it != cha_dependency_map_.end(); ) { 575 // Use unsafe to avoid locking since the allocator is going to be deleted. 576 if (linear_alloc->ContainsUnsafe(it->first)) { 577 // About to delete the ArtMethod, erase the entry from the map. 578 it = cha_dependency_map_.erase(it); 579 } else { 580 ++it; 581 } 582 } 583 } 584 585 } // namespace art 586