intern_table.cc revision d910fcef539e12ab181e56ec80684f39c4e95733
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/space/image_space.h"
22#include "mirror/dex_cache.h"
23#include "mirror/object_array-inl.h"
24#include "mirror/object-inl.h"
25#include "mirror/string-inl.h"
26#include "thread.h"
27#include "utf.h"
28
29namespace art {
30
31InternTable::InternTable()
32    : log_new_roots_(false), allow_new_interns_(true),
33      new_intern_condition_("New intern condition", *Locks::intern_table_lock_) {
34}
35
36size_t InternTable::Size() const {
37  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
38  return strong_interns_.size() + weak_interns_.size();
39}
40
41size_t InternTable::StrongSize() const {
42  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
43  return strong_interns_.size();
44}
45
46size_t InternTable::WeakSize() const {
47  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
48  return weak_interns_.size();
49}
50
51void InternTable::DumpForSigQuit(std::ostream& os) const {
52  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
53  os << "Intern table: " << strong_interns_.size() << " strong; "
54     << weak_interns_.size() << " weak\n";
55}
56
57void InternTable::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
58  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
59  if ((flags & kVisitRootFlagAllRoots) != 0) {
60    for (auto& strong_intern : strong_interns_) {
61      const_cast<GcRoot<mirror::String>&>(strong_intern).
62          VisitRoot(callback, arg, 0, kRootInternedString);
63      DCHECK(!strong_intern.IsNull());
64    }
65  } else if ((flags & kVisitRootFlagNewRoots) != 0) {
66    for (auto& root : new_strong_intern_roots_) {
67      mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
68      root.VisitRoot(callback, arg, 0, kRootInternedString);
69      mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
70      if (UNLIKELY(new_ref != old_ref)) {
71        // The GC moved a root in the log. Need to search the strong interns and update the
72        // corresponding object. This is slow, but luckily for us, this may only happen with a
73        // concurrent moving GC.
74        auto it = strong_interns_.find(GcRoot<mirror::String>(old_ref));
75        DCHECK(it != strong_interns_.end());
76        strong_interns_.erase(it);
77        strong_interns_.insert(GcRoot<mirror::String>(new_ref));
78      }
79    }
80  }
81
82  if ((flags & kVisitRootFlagClearRootLog) != 0) {
83    new_strong_intern_roots_.clear();
84  }
85  if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
86    log_new_roots_ = true;
87  } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
88    log_new_roots_ = false;
89  }
90  // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
91}
92
93mirror::String* InternTable::LookupStrong(mirror::String* s) {
94  return Lookup(&strong_interns_, s);
95}
96
97mirror::String* InternTable::LookupWeak(mirror::String* s) {
98  // Weak interns need a read barrier because they are weak roots.
99  return Lookup(&weak_interns_, s);
100}
101
102mirror::String* InternTable::Lookup(Table* table, mirror::String* s) {
103  Locks::intern_table_lock_->AssertHeld(Thread::Current());
104  auto it = table->find(GcRoot<mirror::String>(s));
105  if (LIKELY(it != table->end())) {
106    return const_cast<GcRoot<mirror::String>&>(*it).Read<kWithReadBarrier>();
107  }
108  return nullptr;
109}
110
111mirror::String* InternTable::InsertStrong(mirror::String* s) {
112  Runtime* runtime = Runtime::Current();
113  if (runtime->IsActiveTransaction()) {
114    runtime->RecordStrongStringInsertion(s);
115  }
116  if (log_new_roots_) {
117    new_strong_intern_roots_.push_back(GcRoot<mirror::String>(s));
118  }
119  strong_interns_.insert(GcRoot<mirror::String>(s));
120  return s;
121}
122
123mirror::String* InternTable::InsertWeak(mirror::String* s) {
124  Runtime* runtime = Runtime::Current();
125  if (runtime->IsActiveTransaction()) {
126    runtime->RecordWeakStringInsertion(s);
127  }
128  weak_interns_.insert(GcRoot<mirror::String>(s));
129  return s;
130}
131
132void InternTable::RemoveStrong(mirror::String* s) {
133  Remove(&strong_interns_, s);
134}
135
136void InternTable::RemoveWeak(mirror::String* s) {
137  Runtime* runtime = Runtime::Current();
138  if (runtime->IsActiveTransaction()) {
139    runtime->RecordWeakStringRemoval(s);
140  }
141  Remove(&weak_interns_, s);
142}
143
144void InternTable::Remove(Table* table, mirror::String* s) {
145  auto it = table->find(GcRoot<mirror::String>(s));
146  DCHECK(it != table->end());
147  table->erase(it);
148}
149
150// Insert/remove methods used to undo changes made during an aborted transaction.
151mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s) {
152  DCHECK(!Runtime::Current()->IsActiveTransaction());
153  return InsertStrong(s);
154}
155mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s) {
156  DCHECK(!Runtime::Current()->IsActiveTransaction());
157  return InsertWeak(s);
158}
159void InternTable::RemoveStrongFromTransaction(mirror::String* s) {
160  DCHECK(!Runtime::Current()->IsActiveTransaction());
161  RemoveStrong(s);
162}
163void InternTable::RemoveWeakFromTransaction(mirror::String* s) {
164  DCHECK(!Runtime::Current()->IsActiveTransaction());
165  RemoveWeak(s);
166}
167
168static mirror::String* LookupStringFromImage(mirror::String* s)
169    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
170  gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
171  if (image == NULL) {
172    return NULL;  // No image present.
173  }
174  mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
175  mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
176  const std::string utf8 = s->ToModifiedUtf8();
177  for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
178    mirror::DexCache* dex_cache = dex_caches->Get(i);
179    const DexFile* dex_file = dex_cache->GetDexFile();
180    // Binary search the dex file for the string index.
181    const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
182    if (string_id != NULL) {
183      uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
184      mirror::String* image = dex_cache->GetResolvedString(string_idx);
185      if (image != NULL) {
186        return image;
187      }
188    }
189  }
190  return NULL;
191}
192
193void InternTable::AllowNewInterns() {
194  Thread* self = Thread::Current();
195  MutexLock mu(self, *Locks::intern_table_lock_);
196  allow_new_interns_ = true;
197  new_intern_condition_.Broadcast(self);
198}
199
200void InternTable::DisallowNewInterns() {
201  Thread* self = Thread::Current();
202  MutexLock mu(self, *Locks::intern_table_lock_);
203  allow_new_interns_ = false;
204}
205
206mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
207  Thread* self = Thread::Current();
208  MutexLock mu(self, *Locks::intern_table_lock_);
209
210  DCHECK(s != NULL);
211
212  while (UNLIKELY(!allow_new_interns_)) {
213    new_intern_condition_.WaitHoldingLocks(self);
214  }
215
216  if (is_strong) {
217    // Check the strong table for a match.
218    mirror::String* strong = LookupStrong(s);
219    if (strong != NULL) {
220      return strong;
221    }
222
223    // Check the image for a match.
224    mirror::String* image = LookupStringFromImage(s);
225    if (image != NULL) {
226      return InsertStrong(image);
227    }
228
229    // There is no match in the strong table, check the weak table.
230    mirror::String* weak = LookupWeak(s);
231    if (weak != NULL) {
232      // A match was found in the weak table. Promote to the strong table.
233      RemoveWeak(weak);
234      return InsertStrong(weak);
235    }
236
237    // No match in the strong table or the weak table. Insert into the strong
238    // table.
239    return InsertStrong(s);
240  }
241
242  // Check the strong table for a match.
243  mirror::String* strong = LookupStrong(s);
244  if (strong != NULL) {
245    return strong;
246  }
247  // Check the image for a match.
248  mirror::String* image = LookupStringFromImage(s);
249  if (image != NULL) {
250    return InsertWeak(image);
251  }
252  // Check the weak table for a match.
253  mirror::String* weak = LookupWeak(s);
254  if (weak != NULL) {
255    return weak;
256  }
257  // Insert into the weak table.
258  return InsertWeak(s);
259}
260
261mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
262  DCHECK(utf8_data != nullptr);
263  return InternStrong(mirror::String::AllocFromModifiedUtf8(
264      Thread::Current(), utf16_length, utf8_data));
265}
266
267mirror::String* InternTable::InternStrong(const char* utf8_data) {
268  DCHECK(utf8_data != nullptr);
269  return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data));
270}
271
272mirror::String* InternTable::InternStrong(mirror::String* s) {
273  if (s == nullptr) {
274    return nullptr;
275  }
276  return Insert(s, true);
277}
278
279mirror::String* InternTable::InternWeak(mirror::String* s) {
280  if (s == nullptr) {
281    return nullptr;
282  }
283  return Insert(s, false);
284}
285
286bool InternTable::ContainsWeak(mirror::String* s) {
287  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
288  const mirror::String* found = LookupWeak(s);
289  return found == s;
290}
291
292void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) {
293  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
294  for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
295    // This does not need a read barrier because this is called by GC.
296    GcRoot<mirror::String>& root = const_cast<GcRoot<mirror::String>&>(*it);
297    mirror::Object* object = root.Read<kWithoutReadBarrier>();
298    mirror::Object* new_object = callback(object, arg);
299    if (new_object == nullptr) {
300      it = weak_interns_.erase(it);
301    } else {
302      root.Assign(down_cast<mirror::String*>(new_object));
303      ++it;
304    }
305  }
306}
307
308std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) {
309  if (kIsDebugBuild) {
310    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
311  }
312  return static_cast<size_t>(
313      const_cast<GcRoot<mirror::String>&>(root).Read<kWithoutReadBarrier>()->GetHashCode());
314}
315
316bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
317                                               const GcRoot<mirror::String>& b) {
318  if (kIsDebugBuild) {
319    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
320  }
321  return const_cast<GcRoot<mirror::String>&>(a).Read<kWithoutReadBarrier>()->Equals(
322      const_cast<GcRoot<mirror::String>&>(b).Read<kWithoutReadBarrier>());
323}
324
325}  // namespace art
326