Home | History | Annotate | Download | only in runtime
      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