intern_table.cc revision 590fee9e8972f872301c2d16a575d579ee564bee
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 "gc/space/image_space.h" 20#include "mirror/dex_cache.h" 21#include "mirror/object_array-inl.h" 22#include "mirror/object-inl.h" 23#include "mirror/string.h" 24#include "thread.h" 25#include "UniquePtr.h" 26#include "utf.h" 27 28namespace art { 29 30InternTable::InternTable() 31 : intern_table_lock_("InternTable lock"), is_dirty_(false), allow_new_interns_(true), 32 new_intern_condition_("New intern condition", intern_table_lock_) { 33} 34 35size_t InternTable::Size() const { 36 MutexLock mu(Thread::Current(), intern_table_lock_); 37 return strong_interns_.size() + weak_interns_.size(); 38} 39 40void InternTable::DumpForSigQuit(std::ostream& os) const { 41 MutexLock mu(Thread::Current(), intern_table_lock_); 42 os << "Intern table: " << strong_interns_.size() << " strong; " 43 << weak_interns_.size() << " weak\n"; 44} 45 46void InternTable::VisitRoots(RootVisitor* visitor, void* arg, 47 bool only_dirty, bool clean_dirty) { 48 MutexLock mu(Thread::Current(), intern_table_lock_); 49 if (!only_dirty || is_dirty_) { 50 for (auto& strong_intern : strong_interns_) { 51 strong_intern.second = down_cast<mirror::String*>(visitor(strong_intern.second, arg)); 52 DCHECK(strong_intern.second != nullptr); 53 } 54 55 if (clean_dirty) { 56 is_dirty_ = false; 57 } 58 } 59 // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. 60} 61 62mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) { 63 intern_table_lock_.AssertHeld(Thread::Current()); 64 for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) { 65 mirror::String* existing_string = it->second; 66 if (existing_string->Equals(s)) { 67 return existing_string; 68 } 69 } 70 return NULL; 71} 72 73mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) { 74 intern_table_lock_.AssertHeld(Thread::Current()); 75 table.insert(std::make_pair(hash_code, s)); 76 return s; 77} 78 79void InternTable::Remove(Table& table, const mirror::String* s, 80 uint32_t hash_code) { 81 intern_table_lock_.AssertHeld(Thread::Current()); 82 for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) { 83 if (it->second == s) { 84 table.erase(it); 85 return; 86 } 87 } 88} 89 90static mirror::String* LookupStringFromImage(mirror::String* s) 91 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 92 gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace(); 93 if (image == NULL) { 94 return NULL; // No image present. 95 } 96 mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); 97 mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>(); 98 const std::string utf8 = s->ToModifiedUtf8(); 99 for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { 100 mirror::DexCache* dex_cache = dex_caches->Get(i); 101 const DexFile* dex_file = dex_cache->GetDexFile(); 102 // Binary search the dex file for the string index. 103 const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str()); 104 if (string_id != NULL) { 105 uint32_t string_idx = dex_file->GetIndexForStringId(*string_id); 106 mirror::String* image = dex_cache->GetResolvedString(string_idx); 107 if (image != NULL) { 108 return image; 109 } 110 } 111 } 112 return NULL; 113} 114 115void InternTable::AllowNewInterns() { 116 Thread* self = Thread::Current(); 117 MutexLock mu(self, intern_table_lock_); 118 allow_new_interns_ = true; 119 new_intern_condition_.Broadcast(self); 120} 121 122void InternTable::DisallowNewInterns() { 123 Thread* self = Thread::Current(); 124 MutexLock mu(self, intern_table_lock_); 125 allow_new_interns_ = false; 126} 127 128mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { 129 Thread* self = Thread::Current(); 130 MutexLock mu(self, intern_table_lock_); 131 132 DCHECK(s != NULL); 133 uint32_t hash_code = s->GetHashCode(); 134 135 while (UNLIKELY(!allow_new_interns_)) { 136 new_intern_condition_.WaitHoldingLocks(self); 137 } 138 139 if (is_strong) { 140 // Check the strong table for a match. 141 mirror::String* strong = Lookup(strong_interns_, s, hash_code); 142 if (strong != NULL) { 143 return strong; 144 } 145 146 // Mark as dirty so that we rescan the roots. 147 is_dirty_ = true; 148 149 // Check the image for a match. 150 mirror::String* image = LookupStringFromImage(s); 151 if (image != NULL) { 152 return Insert(strong_interns_, image, hash_code); 153 } 154 155 // There is no match in the strong table, check the weak table. 156 mirror::String* weak = Lookup(weak_interns_, s, hash_code); 157 if (weak != NULL) { 158 // A match was found in the weak table. Promote to the strong table. 159 Remove(weak_interns_, weak, hash_code); 160 return Insert(strong_interns_, weak, hash_code); 161 } 162 163 // No match in the strong table or the weak table. Insert into the strong 164 // table. 165 return Insert(strong_interns_, s, hash_code); 166 } 167 168 // Check the strong table for a match. 169 mirror::String* strong = Lookup(strong_interns_, s, hash_code); 170 if (strong != NULL) { 171 return strong; 172 } 173 // Check the image for a match. 174 mirror::String* image = LookupStringFromImage(s); 175 if (image != NULL) { 176 return Insert(weak_interns_, image, hash_code); 177 } 178 // Check the weak table for a match. 179 mirror::String* weak = Lookup(weak_interns_, s, hash_code); 180 if (weak != NULL) { 181 return weak; 182 } 183 // Insert into the weak table. 184 return Insert(weak_interns_, s, hash_code); 185} 186 187mirror::String* InternTable::InternStrong(int32_t utf16_length, 188 const char* utf8_data) { 189 return InternStrong(mirror::String::AllocFromModifiedUtf8( 190 Thread::Current(), utf16_length, utf8_data)); 191} 192 193mirror::String* InternTable::InternStrong(const char* utf8_data) { 194 return InternStrong( 195 mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data)); 196} 197 198mirror::String* InternTable::InternStrong(mirror::String* s) { 199 if (s == NULL) { 200 return NULL; 201 } 202 return Insert(s, true); 203} 204 205mirror::String* InternTable::InternWeak(mirror::String* s) { 206 if (s == NULL) { 207 return NULL; 208 } 209 return Insert(s, false); 210} 211 212bool InternTable::ContainsWeak(mirror::String* s) { 213 MutexLock mu(Thread::Current(), intern_table_lock_); 214 const mirror::String* found = Lookup(weak_interns_, s, s->GetHashCode()); 215 return found == s; 216} 217 218void InternTable::SweepInternTableWeaks(RootVisitor visitor, void* arg) { 219 MutexLock mu(Thread::Current(), intern_table_lock_); 220 for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) { 221 mirror::Object* object = it->second; 222 mirror::Object* new_object = visitor(object, arg); 223 if (new_object == nullptr) { 224 // TODO: use it = weak_interns_.erase(it) when we get a c++11 stl. 225 weak_interns_.erase(it++); 226 } else { 227 it->second = down_cast<mirror::String*>(new_object); 228 ++it; 229 } 230 } 231} 232 233} // namespace art 234