29 #include "gc_interface/collectedHeap.inline.hpp" |
29 #include "gc_interface/collectedHeap.inline.hpp" |
30 #include "memory/filemap.hpp" |
30 #include "memory/filemap.hpp" |
31 #include "memory/gcLocker.inline.hpp" |
31 #include "memory/gcLocker.inline.hpp" |
32 #include "oops/oop.inline.hpp" |
32 #include "oops/oop.inline.hpp" |
33 #include "oops/oop.inline2.hpp" |
33 #include "oops/oop.inline2.hpp" |
34 #include "oops/symbolKlass.hpp" |
|
35 #include "runtime/mutexLocker.hpp" |
34 #include "runtime/mutexLocker.hpp" |
36 #include "utilities/hashtable.inline.hpp" |
35 #include "utilities/hashtable.inline.hpp" |
37 |
36 |
38 // -------------------------------------------------------------------------- |
37 // -------------------------------------------------------------------------- |
39 |
38 |
40 SymbolTable* SymbolTable::_the_table = NULL; |
39 SymbolTable* SymbolTable::_the_table = NULL; |
41 |
40 |
|
41 Symbol* SymbolTable::allocate_symbol(const u1* name, int len, TRAPS) { |
|
42 // Don't allow symbols to be created which cannot fit in a Symbol*. |
|
43 if (len > Symbol::max_length()) { |
|
44 THROW_MSG_0(vmSymbols::java_lang_InternalError(), |
|
45 "name is too long to represent"); |
|
46 } |
|
47 Symbol* sym = new (len) Symbol(name, len); |
|
48 assert(sym != NULL, "new should call vm_exit_out_of_memory if C_HEAP is exhausted"); |
|
49 return sym; |
|
50 } |
|
51 |
|
52 bool SymbolTable::allocate_symbols(int names_count, const u1** names, |
|
53 int* lengths, Symbol** syms, TRAPS) { |
|
54 for (int i = 0; i< names_count; i++) { |
|
55 if (lengths[i] > Symbol::max_length()) { |
|
56 THROW_MSG_0(vmSymbols::java_lang_InternalError(), |
|
57 "name is too long to represent"); |
|
58 } |
|
59 } |
|
60 |
|
61 for (int i = 0; i< names_count; i++) { |
|
62 int len = lengths[i]; |
|
63 syms[i] = new (len) Symbol(names[i], len); |
|
64 assert(syms[i] != NULL, "new should call vm_exit_out_of_memory if " |
|
65 "C_HEAP is exhausted"); |
|
66 } |
|
67 return true; |
|
68 } |
|
69 |
|
70 // Call function for all symbols in the symbol table. |
|
71 void SymbolTable::symbols_do(SymbolClosure *cl) { |
|
72 const int n = the_table()->table_size(); |
|
73 for (int i = 0; i < n; i++) { |
|
74 for (HashtableEntry<Symbol*>* p = the_table()->bucket(i); |
|
75 p != NULL; |
|
76 p = p->next()) { |
|
77 cl->do_symbol(p->literal_addr()); |
|
78 } |
|
79 } |
|
80 } |
|
81 |
|
82 int SymbolTable::symbols_removed = 0; |
|
83 int SymbolTable::symbols_counted = 0; |
|
84 |
|
85 // Remove unreferenced symbols from the symbol table |
|
86 // This is done late during GC. This doesn't use the hash table unlink because |
|
87 // it assumes that the literals are oops. |
|
88 void SymbolTable::unlink() { |
|
89 int removed = 0; |
|
90 int total = 0; |
|
91 int memory_total = 0; |
|
92 for (int i = 0; i < the_table()->table_size(); ++i) { |
|
93 for (HashtableEntry<Symbol*>** p = the_table()->bucket_addr(i); *p != NULL; ) { |
|
94 HashtableEntry<Symbol*>* entry = *p; |
|
95 if (entry->is_shared()) { |
|
96 break; |
|
97 } |
|
98 Symbol* s = entry->literal(); |
|
99 memory_total += s->object_size(); |
|
100 total++; |
|
101 assert(s != NULL, "just checking"); |
|
102 // If reference count is zero, remove. |
|
103 if (s->refcount() == 0) { |
|
104 delete s; |
|
105 removed++; |
|
106 *p = entry->next(); |
|
107 the_table()->free_entry(entry); |
|
108 } else { |
|
109 p = entry->next_addr(); |
|
110 } |
|
111 } |
|
112 } |
|
113 symbols_removed += removed; |
|
114 symbols_counted += total; |
|
115 if (PrintGCDetails) { |
|
116 gclog_or_tty->print(" [Symbols=%d size=%dK] ", total, |
|
117 (memory_total*HeapWordSize)/1024); |
|
118 } |
|
119 } |
|
120 |
|
121 |
42 // Lookup a symbol in a bucket. |
122 // Lookup a symbol in a bucket. |
43 |
123 |
44 symbolOop SymbolTable::lookup(int index, const char* name, |
124 Symbol* SymbolTable::lookup(int index, const char* name, |
45 int len, unsigned int hash) { |
125 int len, unsigned int hash) { |
46 for (HashtableEntry* e = bucket(index); e != NULL; e = e->next()) { |
126 for (HashtableEntry<Symbol*>* e = bucket(index); e != NULL; e = e->next()) { |
47 if (e->hash() == hash) { |
127 if (e->hash() == hash) { |
48 symbolOop sym = symbolOop(e->literal()); |
128 Symbol* sym = e->literal(); |
49 if (sym->equals(name, len)) { |
129 if (sym->equals(name, len)) { |
|
130 // something is referencing this symbol now. |
|
131 sym->increment_refcount(); |
50 return sym; |
132 return sym; |
51 } |
133 } |
52 } |
134 } |
53 } |
135 } |
54 return NULL; |
136 return NULL; |
60 // symboltable is used during compilation (VM_thread) The lock free |
142 // symboltable is used during compilation (VM_thread) The lock free |
61 // synchronization is simplified by the fact that we do not delete |
143 // synchronization is simplified by the fact that we do not delete |
62 // entries in the symbol table during normal execution (only during |
144 // entries in the symbol table during normal execution (only during |
63 // safepoints). |
145 // safepoints). |
64 |
146 |
65 symbolOop SymbolTable::lookup(const char* name, int len, TRAPS) { |
147 Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { |
66 unsigned int hashValue = hash_symbol(name, len); |
148 unsigned int hashValue = hash_symbol(name, len); |
67 int index = the_table()->hash_to_index(hashValue); |
149 int index = the_table()->hash_to_index(hashValue); |
68 |
150 |
69 symbolOop s = the_table()->lookup(index, name, len, hashValue); |
151 Symbol* s = the_table()->lookup(index, name, len, hashValue); |
70 |
152 |
71 // Found |
153 // Found |
72 if (s != NULL) return s; |
154 if (s != NULL) return s; |
73 |
155 |
74 // Otherwise, add to symbol to table |
156 // Otherwise, add to symbol to table |
75 return the_table()->basic_add(index, (u1*)name, len, hashValue, CHECK_NULL); |
157 return the_table()->basic_add(index, (u1*)name, len, hashValue, CHECK_NULL); |
76 } |
158 } |
77 |
159 |
78 symbolOop SymbolTable::lookup(symbolHandle sym, int begin, int end, TRAPS) { |
160 Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) { |
79 char* buffer; |
161 char* buffer; |
80 int index, len; |
162 int index, len; |
81 unsigned int hashValue; |
163 unsigned int hashValue; |
82 char* name; |
164 char* name; |
83 { |
165 { |
109 // ResourceMark. |
191 // ResourceMark. |
110 |
192 |
111 return the_table()->basic_add(index, (u1*)buffer, len, hashValue, CHECK_NULL); |
193 return the_table()->basic_add(index, (u1*)buffer, len, hashValue, CHECK_NULL); |
112 } |
194 } |
113 |
195 |
114 symbolOop SymbolTable::lookup_only(const char* name, int len, |
196 Symbol* SymbolTable::lookup_only(const char* name, int len, |
115 unsigned int& hash) { |
197 unsigned int& hash) { |
116 hash = hash_symbol(name, len); |
198 hash = hash_symbol(name, len); |
117 int index = the_table()->hash_to_index(hash); |
199 int index = the_table()->hash_to_index(hash); |
118 |
200 |
119 return the_table()->lookup(index, name, len, hash); |
201 Symbol* s = the_table()->lookup(index, name, len, hash); |
|
202 return s; |
120 } |
203 } |
121 |
204 |
122 // Suggestion: Push unicode-based lookup all the way into the hashing |
205 // Suggestion: Push unicode-based lookup all the way into the hashing |
123 // and probing logic, so there is no need for convert_to_utf8 until |
206 // and probing logic, so there is no need for convert_to_utf8 until |
124 // an actual new symbolOop is created. |
207 // an actual new Symbol* is created. |
125 symbolOop SymbolTable::lookup_unicode(const jchar* name, int utf16_length, TRAPS) { |
208 Symbol* SymbolTable::lookup_unicode(const jchar* name, int utf16_length, TRAPS) { |
126 int utf8_length = UNICODE::utf8_length((jchar*) name, utf16_length); |
209 int utf8_length = UNICODE::utf8_length((jchar*) name, utf16_length); |
127 char stack_buf[128]; |
210 char stack_buf[128]; |
128 if (utf8_length < (int) sizeof(stack_buf)) { |
211 if (utf8_length < (int) sizeof(stack_buf)) { |
129 char* chars = stack_buf; |
212 char* chars = stack_buf; |
130 UNICODE::convert_to_utf8(name, utf16_length, chars); |
213 UNICODE::convert_to_utf8(name, utf16_length, chars); |
161 cp_indices, hashValues, CHECK); |
244 cp_indices, hashValues, CHECK); |
162 if (!added) { |
245 if (!added) { |
163 // do it the hard way |
246 // do it the hard way |
164 for (int i=0; i<names_count; i++) { |
247 for (int i=0; i<names_count; i++) { |
165 int index = table->hash_to_index(hashValues[i]); |
248 int index = table->hash_to_index(hashValues[i]); |
166 symbolOop sym = table->basic_add(index, (u1*)names[i], lengths[i], |
249 Symbol* sym = table->basic_add(index, (u1*)names[i], lengths[i], |
167 hashValues[i], CHECK); |
250 hashValues[i], CHECK); |
168 cp->symbol_at_put(cp_indices[i], sym); |
251 cp->symbol_at_put(cp_indices[i], sym); |
169 } |
252 } |
170 } |
253 } |
171 } |
254 } |
172 |
255 |
173 symbolOop SymbolTable::basic_add(int index, u1 *name, int len, |
256 Symbol* SymbolTable::basic_add(int index, u1 *name, int len, |
174 unsigned int hashValue, TRAPS) { |
257 unsigned int hashValue, TRAPS) { |
175 assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), |
258 assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), |
176 "proposed name of symbol must be stable"); |
259 "proposed name of symbol must be stable"); |
177 |
260 |
178 // We assume that lookup() has been called already, that it failed, |
261 // We assume that lookup() has been called already, that it failed, |
179 // and symbol was not found. We create the symbol here. |
262 // and symbol was not found. We create the symbol here. |
180 symbolKlass* sk = (symbolKlass*) Universe::symbolKlassObj()->klass_part(); |
263 Symbol* sym = allocate_symbol(name, len, CHECK_NULL); |
181 symbolOop s_oop = sk->allocate_symbol(name, len, CHECK_NULL); |
264 |
182 symbolHandle sym (THREAD, s_oop); |
265 // Allocation must be done before grabbing the SymbolTable_lock lock |
183 |
|
184 // Allocation must be done before grapping the SymbolTable_lock lock |
|
185 MutexLocker ml(SymbolTable_lock, THREAD); |
266 MutexLocker ml(SymbolTable_lock, THREAD); |
186 |
267 |
187 assert(sym->equals((char*)name, len), "symbol must be properly initialized"); |
268 assert(sym->equals((char*)name, len), "symbol must be properly initialized"); |
188 |
269 |
189 // Since look-up was done lock-free, we need to check if another |
270 // Since look-up was done lock-free, we need to check if another |
190 // thread beat us in the race to insert the symbol. |
271 // thread beat us in the race to insert the symbol. |
191 |
272 |
192 symbolOop test = lookup(index, (char*)name, len, hashValue); |
273 Symbol* test = lookup(index, (char*)name, len, hashValue); |
193 if (test != NULL) { |
274 if (test != NULL) { |
194 // A race occurred and another thread introduced the symbol, this one |
275 // A race occurred and another thread introduced the symbol, this one |
195 // will be dropped and collected. |
276 // will be dropped and collected. |
|
277 delete sym; |
|
278 assert(test->refcount() != 0, "lookup should have incremented the count"); |
196 return test; |
279 return test; |
197 } |
280 } |
198 |
281 |
199 HashtableEntry* entry = new_entry(hashValue, sym()); |
282 HashtableEntry<Symbol*>* entry = new_entry(hashValue, sym); |
|
283 sym->increment_refcount(); |
200 add_entry(index, entry); |
284 add_entry(index, entry); |
201 return sym(); |
285 return sym; |
202 } |
286 } |
203 |
287 |
204 bool SymbolTable::basic_add(constantPoolHandle cp, int names_count, |
288 bool SymbolTable::basic_add(constantPoolHandle cp, int names_count, |
205 const char** names, int* lengths, |
289 const char** names, int* lengths, |
206 int* cp_indices, unsigned int* hashValues, |
290 int* cp_indices, unsigned int* hashValues, |
207 TRAPS) { |
291 TRAPS) { |
208 symbolKlass* sk = (symbolKlass*) Universe::symbolKlassObj()->klass_part(); |
292 Symbol* syms[symbol_alloc_batch_size]; |
209 symbolOop sym_oops[symbol_alloc_batch_size]; |
293 bool allocated = allocate_symbols(names_count, (const u1**)names, lengths, |
210 bool allocated = sk->allocate_symbols(names_count, names, lengths, |
294 syms, CHECK_false); |
211 sym_oops, CHECK_false); |
|
212 if (!allocated) { |
295 if (!allocated) { |
213 return false; |
296 return false; |
214 } |
297 } |
215 symbolHandle syms[symbol_alloc_batch_size]; |
|
216 int i; |
|
217 for (i=0; i<names_count; i++) { |
|
218 syms[i] = symbolHandle(THREAD, sym_oops[i]); |
|
219 } |
|
220 |
298 |
221 // Allocation must be done before grabbing the SymbolTable_lock lock |
299 // Allocation must be done before grabbing the SymbolTable_lock lock |
222 MutexLocker ml(SymbolTable_lock, THREAD); |
300 MutexLocker ml(SymbolTable_lock, THREAD); |
223 |
301 |
224 for (i=0; i<names_count; i++) { |
302 for (int i=0; i<names_count; i++) { |
225 assert(syms[i]->equals(names[i], lengths[i]), "symbol must be properly initialized"); |
303 assert(syms[i]->equals(names[i], lengths[i]), "symbol must be properly initialized"); |
226 // Since look-up was done lock-free, we need to check if another |
304 // Since look-up was done lock-free, we need to check if another |
227 // thread beat us in the race to insert the symbol. |
305 // thread beat us in the race to insert the symbol. |
228 int index = hash_to_index(hashValues[i]); |
306 int index = hash_to_index(hashValues[i]); |
229 symbolOop test = lookup(index, names[i], lengths[i], hashValues[i]); |
307 Symbol* test = lookup(index, names[i], lengths[i], hashValues[i]); |
230 if (test != NULL) { |
308 if (test != NULL) { |
231 // A race occurred and another thread introduced the symbol, this one |
309 // A race occurred and another thread introduced the symbol, this one |
232 // will be dropped and collected. Use test instead. |
310 // will be dropped and collected. Use test instead. |
233 cp->symbol_at_put(cp_indices[i], test); |
311 cp->symbol_at_put(cp_indices[i], test); |
|
312 assert(test->refcount() != 0, "lookup should have incremented the count"); |
|
313 delete syms[i]; |
234 } else { |
314 } else { |
235 symbolOop sym = syms[i](); |
315 Symbol* sym = syms[i]; |
236 HashtableEntry* entry = new_entry(hashValues[i], sym); |
316 HashtableEntry<Symbol*>* entry = new_entry(hashValues[i], sym); |
|
317 sym->increment_refcount(); // increment refcount in external hashtable |
237 add_entry(index, entry); |
318 add_entry(index, entry); |
238 cp->symbol_at_put(cp_indices[i], sym); |
319 cp->symbol_at_put(cp_indices[i], sym); |
239 } |
320 } |
240 } |
321 } |
241 |
322 |
277 } |
356 } |
278 |
357 |
279 int total = 0; |
358 int total = 0; |
280 int max_symbols = 0; |
359 int max_symbols = 0; |
281 int out_of_range = 0; |
360 int out_of_range = 0; |
|
361 int memory_total = 0; |
|
362 int count = 0; |
282 for (i = 0; i < the_table()->table_size(); i++) { |
363 for (i = 0; i < the_table()->table_size(); i++) { |
283 HashtableEntry* p = the_table()->bucket(i); |
364 HashtableEntry<Symbol*>* p = the_table()->bucket(i); |
284 for ( ; p != NULL; p = p->next()) { |
365 for ( ; p != NULL; p = p->next()) { |
285 int counter = symbolOop(p->literal())->utf8_length(); |
366 memory_total += p->literal()->object_size(); |
|
367 count++; |
|
368 int counter = p->literal()->utf8_length(); |
286 total += counter; |
369 total += counter; |
287 if (counter < results_length) { |
370 if (counter < results_length) { |
288 results[counter]++; |
371 results[counter]++; |
289 } else { |
372 } else { |
290 out_of_range++; |
373 out_of_range++; |
291 } |
374 } |
292 max_symbols = MAX2(max_symbols, counter); |
375 max_symbols = MAX2(max_symbols, counter); |
293 } |
376 } |
294 } |
377 } |
295 tty->print_cr("Symbol Table:"); |
378 tty->print_cr("Symbol Table:"); |
|
379 tty->print_cr("Total number of symbols %5d", count); |
|
380 tty->print_cr("Total size in memory %5dK", |
|
381 (memory_total*HeapWordSize)/1024); |
|
382 tty->print_cr("Total counted %5d", symbols_counted); |
|
383 tty->print_cr("Total removed %5d", symbols_removed); |
|
384 if (symbols_counted > 0) { |
|
385 tty->print_cr("Percent removed %3.2f", |
|
386 ((float)symbols_removed/(float)symbols_counted)* 100); |
|
387 } |
|
388 tty->print_cr("Reference counts %5d", Symbol::_total_count); |
|
389 tty->print_cr("Histogram of symbol length:"); |
296 tty->print_cr("%8s %5d", "Total ", total); |
390 tty->print_cr("%8s %5d", "Total ", total); |
297 tty->print_cr("%8s %5d", "Maximum", max_symbols); |
391 tty->print_cr("%8s %5d", "Maximum", max_symbols); |
298 tty->print_cr("%8s %3.2f", "Average", |
392 tty->print_cr("%8s %3.2f", "Average", |
299 ((float) total / (float) the_table()->table_size())); |
393 ((float) total / (float) the_table()->table_size())); |
300 tty->print_cr("%s", "Histogram:"); |
394 tty->print_cr("%s", "Histogram:"); |
302 for (i = 0; i < results_length; i++) { |
396 for (i = 0; i < results_length; i++) { |
303 if (results[i] > 0) { |
397 if (results[i] > 0) { |
304 tty->print_cr("%6d %10d", i, results[i]); |
398 tty->print_cr("%6d %10d", i, results[i]); |
305 } |
399 } |
306 } |
400 } |
307 int line_length = 70; |
401 if (Verbose) { |
308 tty->print_cr("%s %30s", " Length", "Number chains that length"); |
402 int line_length = 70; |
309 for (i = 0; i < results_length; i++) { |
403 tty->print_cr("%s %30s", " Length", "Number chains that length"); |
310 if (results[i] > 0) { |
404 for (i = 0; i < results_length; i++) { |
311 tty->print("%4d", i); |
405 if (results[i] > 0) { |
312 for (j = 0; (j < results[i]) && (j < line_length); j++) { |
406 tty->print("%4d", i); |
313 tty->print("%1s", "*"); |
407 for (j = 0; (j < results[i]) && (j < line_length); j++) { |
314 } |
408 tty->print("%1s", "*"); |
315 if (j == line_length) { |
409 } |
316 tty->print("%1s", "+"); |
410 if (j == line_length) { |
317 } |
411 tty->print("%1s", "+"); |
318 tty->cr(); |
412 } |
|
413 tty->cr(); |
|
414 } |
319 } |
415 } |
320 } |
416 } |
321 tty->print_cr(" %s %d: %d\n", "Number chains longer than", |
417 tty->print_cr(" %s %d: %d\n", "Number chains longer than", |
322 results_length, out_of_range); |
418 results_length, out_of_range); |
|
419 } |
|
420 |
|
421 void SymbolTable::print() { |
|
422 for (int i = 0; i < the_table()->table_size(); ++i) { |
|
423 HashtableEntry<Symbol*>** p = the_table()->bucket_addr(i); |
|
424 HashtableEntry<Symbol*>* entry = the_table()->bucket(i); |
|
425 if (entry != NULL) { |
|
426 while (entry != NULL) { |
|
427 tty->print(PTR_FORMAT " ", entry->literal()); |
|
428 entry->literal()->print(); |
|
429 tty->print(" %d", entry->literal()->refcount()); |
|
430 p = entry->next_addr(); |
|
431 entry = (HashtableEntry<Symbol*>*)HashtableEntry<Symbol*>::make_ptr(*p); |
|
432 } |
|
433 tty->cr(); |
|
434 } |
|
435 } |
323 } |
436 } |
324 |
437 |
325 #endif // PRODUCT |
438 #endif // PRODUCT |
326 |
439 |
327 // -------------------------------------------------------------------------- |
440 // -------------------------------------------------------------------------- |
498 Handle string; |
611 Handle string; |
499 oop result = intern(string, chars, length, CHECK_NULL); |
612 oop result = intern(string, chars, length, CHECK_NULL); |
500 return result; |
613 return result; |
501 } |
614 } |
502 |
615 |
|
616 void StringTable::unlink(BoolObjectClosure* is_alive) { |
|
617 // Readers of the table are unlocked, so we should only be removing |
|
618 // entries at a safepoint. |
|
619 assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); |
|
620 for (int i = 0; i < the_table()->table_size(); ++i) { |
|
621 for (HashtableEntry<oop>** p = the_table()->bucket_addr(i); *p != NULL; ) { |
|
622 HashtableEntry<oop>* entry = *p; |
|
623 if (entry->is_shared()) { |
|
624 break; |
|
625 } |
|
626 assert(entry->literal() != NULL, "just checking"); |
|
627 if (is_alive->do_object_b(entry->literal())) { |
|
628 p = entry->next_addr(); |
|
629 } else { |
|
630 *p = entry->next(); |
|
631 the_table()->free_entry(entry); |
|
632 } |
|
633 } |
|
634 } |
|
635 } |
|
636 |
|
637 void StringTable::oops_do(OopClosure* f) { |
|
638 for (int i = 0; i < the_table()->table_size(); ++i) { |
|
639 HashtableEntry<oop>** p = the_table()->bucket_addr(i); |
|
640 HashtableEntry<oop>* entry = the_table()->bucket(i); |
|
641 while (entry != NULL) { |
|
642 f->do_oop((oop*)entry->literal_addr()); |
|
643 |
|
644 // Did the closure remove the literal from the table? |
|
645 if (entry->literal() == NULL) { |
|
646 assert(!entry->is_shared(), "immutable hashtable entry?"); |
|
647 *p = entry->next(); |
|
648 the_table()->free_entry(entry); |
|
649 } else { |
|
650 p = entry->next_addr(); |
|
651 } |
|
652 entry = (HashtableEntry<oop>*)HashtableEntry<oop>::make_ptr(*p); |
|
653 } |
|
654 } |
|
655 } |
|
656 |
503 void StringTable::verify() { |
657 void StringTable::verify() { |
504 for (int i = 0; i < the_table()->table_size(); ++i) { |
658 for (int i = 0; i < the_table()->table_size(); ++i) { |
505 HashtableEntry* p = the_table()->bucket(i); |
659 HashtableEntry<oop>* p = the_table()->bucket(i); |
506 for ( ; p != NULL; p = p->next()) { |
660 for ( ; p != NULL; p = p->next()) { |
507 oop s = p->literal(); |
661 oop s = p->literal(); |
508 guarantee(s != NULL, "interned string is NULL"); |
662 guarantee(s != NULL, "interned string is NULL"); |
509 guarantee(s->is_perm(), "interned string not in permspace"); |
663 guarantee(s->is_perm(), "interned string not in permspace"); |
510 |
664 |