1 /* 2 * Copyright (C) 2011 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 "intern_table.h" 18 19 #include <memory> 20 21 #include "gc_root-inl.h" 22 #include "gc/collector/garbage_collector.h" 23 #include "gc/space/image_space.h" 24 #include "gc/weak_root_state.h" 25 #include "image-inl.h" 26 #include "mirror/dex_cache-inl.h" 27 #include "mirror/object_array-inl.h" 28 #include "mirror/object-inl.h" 29 #include "mirror/string-inl.h" 30 #include "thread.h" 31 #include "utf.h" 32 33 namespace art { 34 35 InternTable::InternTable() 36 : log_new_roots_(false), 37 weak_intern_condition_("New intern condition", *Locks::intern_table_lock_), 38 weak_root_state_(gc::kWeakRootStateNormal) { 39 } 40 41 size_t InternTable::Size() const { 42 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 43 return strong_interns_.Size() + weak_interns_.Size(); 44 } 45 46 size_t InternTable::StrongSize() const { 47 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 48 return strong_interns_.Size(); 49 } 50 51 size_t InternTable::WeakSize() const { 52 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 53 return weak_interns_.Size(); 54 } 55 56 void InternTable::DumpForSigQuit(std::ostream& os) const { 57 os << "Intern table: " << StrongSize() << " strong; " << WeakSize() << " weak\n"; 58 } 59 60 void InternTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { 61 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 62 if ((flags & kVisitRootFlagAllRoots) != 0) { 63 strong_interns_.VisitRoots(visitor); 64 } else if ((flags & kVisitRootFlagNewRoots) != 0) { 65 for (auto& root : new_strong_intern_roots_) { 66 ObjPtr<mirror::String> old_ref = root.Read<kWithoutReadBarrier>(); 67 root.VisitRoot(visitor, RootInfo(kRootInternedString)); 68 ObjPtr<mirror::String> new_ref = root.Read<kWithoutReadBarrier>(); 69 if (new_ref != old_ref) { 70 // The GC moved a root in the log. Need to search the strong interns and update the 71 // corresponding object. This is slow, but luckily for us, this may only happen with a 72 // concurrent moving GC. 73 strong_interns_.Remove(old_ref); 74 strong_interns_.Insert(new_ref); 75 } 76 } 77 } 78 if ((flags & kVisitRootFlagClearRootLog) != 0) { 79 new_strong_intern_roots_.clear(); 80 } 81 if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { 82 log_new_roots_ = true; 83 } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { 84 log_new_roots_ = false; 85 } 86 // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. 87 } 88 89 ObjPtr<mirror::String> InternTable::LookupWeak(Thread* self, ObjPtr<mirror::String> s) { 90 MutexLock mu(self, *Locks::intern_table_lock_); 91 return LookupWeakLocked(s); 92 } 93 94 ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self, ObjPtr<mirror::String> s) { 95 MutexLock mu(self, *Locks::intern_table_lock_); 96 return LookupStrongLocked(s); 97 } 98 99 ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self, 100 uint32_t utf16_length, 101 const char* utf8_data) { 102 DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data)); 103 Utf8String string(utf16_length, 104 utf8_data, 105 ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length)); 106 MutexLock mu(self, *Locks::intern_table_lock_); 107 return strong_interns_.Find(string); 108 } 109 110 ObjPtr<mirror::String> InternTable::LookupWeakLocked(ObjPtr<mirror::String> s) { 111 return weak_interns_.Find(s); 112 } 113 114 ObjPtr<mirror::String> InternTable::LookupStrongLocked(ObjPtr<mirror::String> s) { 115 return strong_interns_.Find(s); 116 } 117 118 void InternTable::AddNewTable() { 119 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 120 weak_interns_.AddNewTable(); 121 strong_interns_.AddNewTable(); 122 } 123 124 ObjPtr<mirror::String> InternTable::InsertStrong(ObjPtr<mirror::String> s) { 125 Runtime* runtime = Runtime::Current(); 126 if (runtime->IsActiveTransaction()) { 127 runtime->RecordStrongStringInsertion(s); 128 } 129 if (log_new_roots_) { 130 new_strong_intern_roots_.push_back(GcRoot<mirror::String>(s)); 131 } 132 strong_interns_.Insert(s); 133 return s; 134 } 135 136 ObjPtr<mirror::String> InternTable::InsertWeak(ObjPtr<mirror::String> s) { 137 Runtime* runtime = Runtime::Current(); 138 if (runtime->IsActiveTransaction()) { 139 runtime->RecordWeakStringInsertion(s); 140 } 141 weak_interns_.Insert(s); 142 return s; 143 } 144 145 void InternTable::RemoveStrong(ObjPtr<mirror::String> s) { 146 strong_interns_.Remove(s); 147 } 148 149 void InternTable::RemoveWeak(ObjPtr<mirror::String> s) { 150 Runtime* runtime = Runtime::Current(); 151 if (runtime->IsActiveTransaction()) { 152 runtime->RecordWeakStringRemoval(s); 153 } 154 weak_interns_.Remove(s); 155 } 156 157 // Insert/remove methods used to undo changes made during an aborted transaction. 158 ObjPtr<mirror::String> InternTable::InsertStrongFromTransaction(ObjPtr<mirror::String> s) { 159 DCHECK(!Runtime::Current()->IsActiveTransaction()); 160 return InsertStrong(s); 161 } 162 163 ObjPtr<mirror::String> InternTable::InsertWeakFromTransaction(ObjPtr<mirror::String> s) { 164 DCHECK(!Runtime::Current()->IsActiveTransaction()); 165 return InsertWeak(s); 166 } 167 168 void InternTable::RemoveStrongFromTransaction(ObjPtr<mirror::String> s) { 169 DCHECK(!Runtime::Current()->IsActiveTransaction()); 170 RemoveStrong(s); 171 } 172 173 void InternTable::RemoveWeakFromTransaction(ObjPtr<mirror::String> s) { 174 DCHECK(!Runtime::Current()->IsActiveTransaction()); 175 RemoveWeak(s); 176 } 177 178 void InternTable::AddImagesStringsToTable(const std::vector<gc::space::ImageSpace*>& image_spaces) { 179 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 180 for (gc::space::ImageSpace* image_space : image_spaces) { 181 const ImageHeader* const header = &image_space->GetImageHeader(); 182 // Check if we have the interned strings section. 183 const ImageSection& section = header->GetImageSection(ImageHeader::kSectionInternedStrings); 184 if (section.Size() > 0) { 185 AddTableFromMemoryLocked(image_space->Begin() + section.Offset()); 186 } 187 } 188 } 189 190 void InternTable::BroadcastForNewInterns() { 191 Thread* self = Thread::Current(); 192 MutexLock mu(self, *Locks::intern_table_lock_); 193 weak_intern_condition_.Broadcast(self); 194 } 195 196 void InternTable::WaitUntilAccessible(Thread* self) { 197 Locks::intern_table_lock_->ExclusiveUnlock(self); 198 { 199 ScopedThreadSuspension sts(self, kWaitingWeakGcRootRead); 200 MutexLock mu(self, *Locks::intern_table_lock_); 201 while ((!kUseReadBarrier && weak_root_state_ == gc::kWeakRootStateNoReadsOrWrites) || 202 (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) { 203 weak_intern_condition_.Wait(self); 204 } 205 } 206 Locks::intern_table_lock_->ExclusiveLock(self); 207 } 208 209 ObjPtr<mirror::String> InternTable::Insert(ObjPtr<mirror::String> s, 210 bool is_strong, 211 bool holding_locks) { 212 if (s == nullptr) { 213 return nullptr; 214 } 215 Thread* const self = Thread::Current(); 216 MutexLock mu(self, *Locks::intern_table_lock_); 217 if (kDebugLocking && !holding_locks) { 218 Locks::mutator_lock_->AssertSharedHeld(self); 219 CHECK_EQ(2u, self->NumberOfHeldMutexes()) << "may only safely hold the mutator lock"; 220 } 221 while (true) { 222 if (holding_locks) { 223 if (!kUseReadBarrier) { 224 CHECK_EQ(weak_root_state_, gc::kWeakRootStateNormal); 225 } else { 226 CHECK(self->GetWeakRefAccessEnabled()); 227 } 228 } 229 // Check the strong table for a match. 230 ObjPtr<mirror::String> strong = LookupStrongLocked(s); 231 if (strong != nullptr) { 232 return strong; 233 } 234 if ((!kUseReadBarrier && weak_root_state_ != gc::kWeakRootStateNoReadsOrWrites) || 235 (kUseReadBarrier && self->GetWeakRefAccessEnabled())) { 236 break; 237 } 238 // weak_root_state_ is set to gc::kWeakRootStateNoReadsOrWrites in the GC pause but is only 239 // cleared after SweepSystemWeaks has completed. This is why we need to wait until it is 240 // cleared. 241 CHECK(!holding_locks); 242 StackHandleScope<1> hs(self); 243 auto h = hs.NewHandleWrapper(&s); 244 WaitUntilAccessible(self); 245 } 246 if (!kUseReadBarrier) { 247 CHECK_EQ(weak_root_state_, gc::kWeakRootStateNormal); 248 } else { 249 CHECK(self->GetWeakRefAccessEnabled()); 250 } 251 // There is no match in the strong table, check the weak table. 252 ObjPtr<mirror::String> weak = LookupWeakLocked(s); 253 if (weak != nullptr) { 254 if (is_strong) { 255 // A match was found in the weak table. Promote to the strong table. 256 RemoveWeak(weak); 257 return InsertStrong(weak); 258 } 259 return weak; 260 } 261 // No match in the strong table or the weak table. Insert into the strong / weak table. 262 return is_strong ? InsertStrong(s) : InsertWeak(s); 263 } 264 265 ObjPtr<mirror::String> InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) { 266 DCHECK(utf8_data != nullptr); 267 Thread* self = Thread::Current(); 268 // Try to avoid allocation. 269 ObjPtr<mirror::String> s = LookupStrong(self, utf16_length, utf8_data); 270 if (s != nullptr) { 271 return s; 272 } 273 return InternStrong(mirror::String::AllocFromModifiedUtf8( 274 self, utf16_length, utf8_data)); 275 } 276 277 ObjPtr<mirror::String> InternTable::InternStrong(const char* utf8_data) { 278 DCHECK(utf8_data != nullptr); 279 return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data)); 280 } 281 282 ObjPtr<mirror::String> InternTable::InternStrongImageString(ObjPtr<mirror::String> s) { 283 // May be holding the heap bitmap lock. 284 return Insert(s, true, true); 285 } 286 287 ObjPtr<mirror::String> InternTable::InternStrong(ObjPtr<mirror::String> s) { 288 return Insert(s, true, false); 289 } 290 291 ObjPtr<mirror::String> InternTable::InternWeak(ObjPtr<mirror::String> s) { 292 return Insert(s, false, false); 293 } 294 295 bool InternTable::ContainsWeak(ObjPtr<mirror::String> s) { 296 return LookupWeak(Thread::Current(), s) == s; 297 } 298 299 void InternTable::SweepInternTableWeaks(IsMarkedVisitor* visitor) { 300 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 301 weak_interns_.SweepWeaks(visitor); 302 } 303 304 size_t InternTable::AddTableFromMemory(const uint8_t* ptr) { 305 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 306 return AddTableFromMemoryLocked(ptr); 307 } 308 309 size_t InternTable::AddTableFromMemoryLocked(const uint8_t* ptr) { 310 return strong_interns_.AddTableFromMemory(ptr); 311 } 312 313 size_t InternTable::WriteToMemory(uint8_t* ptr) { 314 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 315 return strong_interns_.WriteToMemory(ptr); 316 } 317 318 std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) const { 319 if (kIsDebugBuild) { 320 Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); 321 } 322 // An additional cast to prevent undesired sign extension. 323 return static_cast<size_t>( 324 static_cast<uint32_t>(root.Read<kWithoutReadBarrier>()->GetHashCode())); 325 } 326 327 bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a, 328 const GcRoot<mirror::String>& b) const { 329 if (kIsDebugBuild) { 330 Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); 331 } 332 return a.Read<kWithoutReadBarrier>()->Equals(b.Read<kWithoutReadBarrier>()); 333 } 334 335 bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a, 336 const Utf8String& b) const { 337 if (kIsDebugBuild) { 338 Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); 339 } 340 ObjPtr<mirror::String> a_string = a.Read<kWithoutReadBarrier>(); 341 uint32_t a_length = static_cast<uint32_t>(a_string->GetLength()); 342 if (a_length != b.GetUtf16Length()) { 343 return false; 344 } 345 if (a_string->IsCompressed()) { 346 size_t b_byte_count = strlen(b.GetUtf8Data()); 347 size_t b_utf8_length = CountModifiedUtf8Chars(b.GetUtf8Data(), b_byte_count); 348 // Modified UTF-8 single byte character range is 0x01 .. 0x7f 349 // The string compression occurs on regular ASCII with same exact range, 350 // not on extended ASCII which up to 0xff 351 const bool is_b_regular_ascii = (b_byte_count == b_utf8_length); 352 if (is_b_regular_ascii) { 353 return memcmp(b.GetUtf8Data(), 354 a_string->GetValueCompressed(), a_length * sizeof(uint8_t)) == 0; 355 } else { 356 return false; 357 } 358 } else { 359 const uint16_t* a_value = a_string->GetValue(); 360 return CompareModifiedUtf8ToUtf16AsCodePointValues(b.GetUtf8Data(), a_value, a_length) == 0; 361 } 362 } 363 364 size_t InternTable::Table::AddTableFromMemory(const uint8_t* ptr) { 365 size_t read_count = 0; 366 UnorderedSet set(ptr, /*make copy*/false, &read_count); 367 if (set.Empty()) { 368 // Avoid inserting empty sets. 369 return read_count; 370 } 371 // TODO: Disable this for app images if app images have intern tables. 372 static constexpr bool kCheckDuplicates = true; 373 if (kCheckDuplicates) { 374 for (GcRoot<mirror::String>& string : set) { 375 CHECK(Find(string.Read()) == nullptr) << "Already found " << string.Read()->ToModifiedUtf8(); 376 } 377 } 378 // Insert at the front since we add new interns into the back. 379 tables_.insert(tables_.begin(), std::move(set)); 380 return read_count; 381 } 382 383 size_t InternTable::Table::WriteToMemory(uint8_t* ptr) { 384 if (tables_.empty()) { 385 return 0; 386 } 387 UnorderedSet* table_to_write; 388 UnorderedSet combined; 389 if (tables_.size() > 1) { 390 table_to_write = &combined; 391 for (UnorderedSet& table : tables_) { 392 for (GcRoot<mirror::String>& string : table) { 393 combined.Insert(string); 394 } 395 } 396 } else { 397 table_to_write = &tables_.back(); 398 } 399 return table_to_write->WriteToMemory(ptr); 400 } 401 402 void InternTable::Table::Remove(ObjPtr<mirror::String> s) { 403 for (UnorderedSet& table : tables_) { 404 auto it = table.Find(GcRoot<mirror::String>(s)); 405 if (it != table.end()) { 406 table.Erase(it); 407 return; 408 } 409 } 410 LOG(FATAL) << "Attempting to remove non-interned string " << s->ToModifiedUtf8(); 411 } 412 413 ObjPtr<mirror::String> InternTable::Table::Find(ObjPtr<mirror::String> s) { 414 Locks::intern_table_lock_->AssertHeld(Thread::Current()); 415 for (UnorderedSet& table : tables_) { 416 auto it = table.Find(GcRoot<mirror::String>(s)); 417 if (it != table.end()) { 418 return it->Read(); 419 } 420 } 421 return nullptr; 422 } 423 424 ObjPtr<mirror::String> InternTable::Table::Find(const Utf8String& string) { 425 Locks::intern_table_lock_->AssertHeld(Thread::Current()); 426 for (UnorderedSet& table : tables_) { 427 auto it = table.Find(string); 428 if (it != table.end()) { 429 return it->Read(); 430 } 431 } 432 return nullptr; 433 } 434 435 void InternTable::Table::AddNewTable() { 436 tables_.push_back(UnorderedSet()); 437 } 438 439 void InternTable::Table::Insert(ObjPtr<mirror::String> s) { 440 // Always insert the last table, the image tables are before and we avoid inserting into these 441 // to prevent dirty pages. 442 DCHECK(!tables_.empty()); 443 tables_.back().Insert(GcRoot<mirror::String>(s)); 444 } 445 446 void InternTable::Table::VisitRoots(RootVisitor* visitor) { 447 BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor( 448 visitor, RootInfo(kRootInternedString)); 449 for (UnorderedSet& table : tables_) { 450 for (auto& intern : table) { 451 buffered_visitor.VisitRoot(intern); 452 } 453 } 454 } 455 456 void InternTable::Table::SweepWeaks(IsMarkedVisitor* visitor) { 457 for (UnorderedSet& table : tables_) { 458 SweepWeaks(&table, visitor); 459 } 460 } 461 462 void InternTable::Table::SweepWeaks(UnorderedSet* set, IsMarkedVisitor* visitor) { 463 for (auto it = set->begin(), end = set->end(); it != end;) { 464 // This does not need a read barrier because this is called by GC. 465 mirror::Object* object = it->Read<kWithoutReadBarrier>(); 466 mirror::Object* new_object = visitor->IsMarked(object); 467 if (new_object == nullptr) { 468 it = set->Erase(it); 469 } else { 470 *it = GcRoot<mirror::String>(new_object->AsString()); 471 ++it; 472 } 473 } 474 } 475 476 size_t InternTable::Table::Size() const { 477 return std::accumulate(tables_.begin(), 478 tables_.end(), 479 0U, 480 [](size_t sum, const UnorderedSet& set) { 481 return sum + set.Size(); 482 }); 483 } 484 485 void InternTable::ChangeWeakRootState(gc::WeakRootState new_state) { 486 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); 487 ChangeWeakRootStateLocked(new_state); 488 } 489 490 void InternTable::ChangeWeakRootStateLocked(gc::WeakRootState new_state) { 491 CHECK(!kUseReadBarrier); 492 weak_root_state_ = new_state; 493 if (new_state != gc::kWeakRootStateNoReadsOrWrites) { 494 weak_intern_condition_.Broadcast(Thread::Current()); 495 } 496 } 497 498 InternTable::Table::Table() { 499 Runtime* const runtime = Runtime::Current(); 500 // Initial table. 501 tables_.push_back(UnorderedSet()); 502 tables_.back().SetLoadFactor(runtime->GetHashTableMinLoadFactor(), 503 runtime->GetHashTableMaxLoadFactor()); 504 } 505 506 } // namespace art 507