< prev index next >

src/hotspot/share/prims/jvmtiTagMapTable.cpp

Print this page

  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "memory/allocation.hpp"
 26 #include "memory/universe.hpp"


 27 #include "oops/oop.inline.hpp"
 28 #include "oops/weakHandle.inline.hpp"
 29 #include "prims/jvmtiExport.hpp"
 30 #include "prims/jvmtiTagMapTable.hpp"
 31 
 32 
 33 JvmtiTagMapKey::JvmtiTagMapKey(oop obj) : _obj(obj) {}












 34 
 35 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src) {
 36   // move object into WeakHandle when copying into the table





























































































 37   if (src._obj != nullptr) {



 38 
 39     // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
 40     // a heap walk.  The object needs to be kept alive when it is published.
 41     Universe::heap()->keep_alive(src._obj);
 42 
 43     _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj);




 44   } else {
 45     // resizing needs to create a copy.
 46     _wh = src._wh;





 47   }
 48   // obj is always null after a copy.
 49   _obj = nullptr;
 50 }
 51 
 52 void JvmtiTagMapKey::release_weak_handle() {
 53   _wh.release(JvmtiExport::weak_tag_storage());
















 54 }
 55 
 56 oop JvmtiTagMapKey::object() const {
 57   assert(_obj == nullptr, "Must have a handle and not object");
 58   return _wh.resolve();
 59 }
 60 
 61 oop JvmtiTagMapKey::object_no_keepalive() const {
 62   assert(_obj == nullptr, "Must have a handle and not object");
 63   return _wh.peek();
















 64 }
 65 
 66 static const int INITIAL_TABLE_SIZE = 1007;
 67 static const int MAX_TABLE_SIZE     = 0x3fffffff;
 68 
 69 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
 70 
 71 void JvmtiTagMapTable::clear() {
 72   struct RemoveAll {
 73     bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
 74       entry.release_weak_handle();
 75       return true;
 76     }
 77   } remove_all;
 78   // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
 79   // while iterating over all the elements of the table. If the do_entry() method returns true the element
 80   // will be removed.
 81   // In this case, we always return true from do_entry to clear all the elements.
 82   _table.unlink(&remove_all);
 83 
 84   assert(_table.number_of_entries() == 0, "should have removed all entries");
 85 }
 86 
 87 JvmtiTagMapTable::~JvmtiTagMapTable() {
 88   clear();
 89 }
 90 
 91 jlong JvmtiTagMapTable::find(oop obj) {
 92   if (is_empty()) {
 93     return 0;
 94   }
 95 
 96   if (obj->fast_no_hash_check()) {
 97     // Objects in the table all have a hashcode.
 98     return 0;


 99   }




100 
101   JvmtiTagMapKey jtme(obj);
102   jlong* found = _table.get(jtme);

103   return found == nullptr ? 0 : *found;
104 }
105 
106 void JvmtiTagMapTable::add(oop obj, jlong tag) {
107   JvmtiTagMapKey new_entry(obj);

108   bool is_added;
109   if (obj->fast_no_hash_check()) {
110     // Can't be in the table so add it fast.
111     is_added = _table.put_when_absent(new_entry, tag);
112   } else {
113     jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
114     *value = tag; // assign the new tag
115   }
116   if (is_added) {
117     if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
118       int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
119       log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
120                               _table.table_size(), _table.number_of_entries(), max_bucket_size);
121     }
122   }
123 }
124 
125 void JvmtiTagMapTable::remove(oop obj) {
126   JvmtiTagMapKey jtme(obj);
127   auto clean = [] (JvmtiTagMapKey& entry, jlong tag) {
128     entry.release_weak_handle();









129   };
130   _table.remove(jtme, clean);
131 }
132 
133 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
134   _table.iterate(closure);
135 }
136 
137 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
138   struct IsDead {
139     GrowableArray<jlong>* _objects;
140     IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
141     bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
142       if (entry.object_no_keepalive() == nullptr) {
143         if (_objects != nullptr) {
144           _objects->append(tag);
145         }
146         entry.release_weak_handle();
147         return true;
148       }
149       return false;;
150     }
151   } is_dead(objects);
152   _table.unlink(&is_dead);
153 }




















































































































  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "memory/allocation.hpp"
 26 #include "memory/universe.hpp"
 27 #include "oops/fieldStreams.inline.hpp"
 28 #include "oops/inlineKlass.hpp"
 29 #include "oops/oop.inline.hpp"
 30 #include "oops/weakHandle.inline.hpp"
 31 #include "prims/jvmtiExport.hpp"
 32 #include "prims/jvmtiTagMapTable.hpp"
 33 
 34 
 35 static unsigned get_value_object_hash(oop holder, int offset, Klass* klass) {
 36   assert(klass->is_inline_klass(), "Must be InlineKlass");
 37   // For inline types, use the klass as a hash code and let the equals match the obj.
 38   // It might have a long bucket but TBD to improve this if a customer situation arises.
 39   return (unsigned)((int64_t)klass >> 3);
 40 }
 41 
 42 static unsigned get_value_object_hash(const JvmtiHeapwalkObject & obj) {
 43   assert(obj.is_value(), "Must be value class");
 44   return get_value_object_hash(obj.obj(), obj.offset(), obj.inline_klass());
 45 }
 46 
 47 static bool equal_oops(oop obj1, oop obj2); // forward declaration
 48 
 49 static bool equal_fields(char type, oop obj1, int offset1, oop obj2, int offset2) {
 50   switch (type) {
 51   case JVM_SIGNATURE_BOOLEAN:
 52     return obj1->bool_field(offset1) == obj2->bool_field(offset2);
 53   case JVM_SIGNATURE_CHAR:
 54     return obj1->char_field(offset1) == obj2->char_field(offset2);
 55   case JVM_SIGNATURE_FLOAT:
 56     return obj1->float_field(offset1) == obj2->float_field(offset2);
 57   case JVM_SIGNATURE_DOUBLE:
 58     return obj1->double_field(offset1) == obj2->double_field(offset2);
 59   case JVM_SIGNATURE_BYTE:
 60     return obj1->byte_field(offset1) == obj2->byte_field(offset2);
 61   case JVM_SIGNATURE_SHORT:
 62     return obj1->short_field(offset1) == obj2->short_field(offset2);
 63   case JVM_SIGNATURE_INT:
 64     return obj1->int_field(offset1) == obj2->int_field(offset2);
 65   case JVM_SIGNATURE_LONG:
 66     return obj1->long_field(offset1) == obj2->long_field(offset2);
 67   case JVM_SIGNATURE_CLASS:
 68   case JVM_SIGNATURE_ARRAY:
 69     return equal_oops(obj1->obj_field(offset1), obj2->obj_field(offset2));
 70   }
 71   ShouldNotReachHere();
 72 }
 73 
 74 static bool is_null_flat_field(oop obj, int offset, InlineKlass* klass) {
 75   return klass->is_payload_marked_as_null(cast_from_oop<address>(obj) + offset);
 76 }
 77 
 78 // For heap-allocated objects offset is 0 and 'klass' is obj1->klass() (== obj2->klass()).
 79 // For flattened objects offset is the offset in the holder object, 'klass' is inlined object class.
 80 // The object must be prechecked for non-null values.
 81 static bool equal_value_objects(oop obj1, int offset1, oop obj2, int offset2, InlineKlass* klass) {
 82   for (JavaFieldStream fld(klass); !fld.done(); fld.next()) {
 83     // ignore static fields
 84     if (fld.access_flags().is_static()) {
 85       continue;
 86     }
 87     int field_offset1 = offset1 + fld.offset() - (offset1 > 0 ? klass->payload_offset() : 0);
 88     int field_offset2 = offset2 + fld.offset() - (offset2 > 0 ? klass->payload_offset() : 0);
 89     if (fld.is_flat()) { // flat value field
 90       InstanceKlass* holder_klass = fld.field_holder();
 91       InlineKlass* field_klass = holder_klass->get_inline_type_field_klass(fld.index());
 92       if (!fld.is_null_free_inline_type()) {
 93         bool field1_is_null = is_null_flat_field(obj1, field_offset1, field_klass);
 94         bool field2_is_null = is_null_flat_field(obj2, field_offset2, field_klass);
 95         if (field1_is_null != field2_is_null) {
 96           return false;
 97         }
 98         if (field1_is_null) { // if both fields are null, go to next field
 99           continue;
100         }
101       }
102 
103       if (!equal_value_objects(obj1, field_offset1, obj2, field_offset2, field_klass)) {
104         return false;
105       }
106     } else {
107       if (!equal_fields(fld.signature()->char_at(0), obj1, field_offset1, obj2, field_offset2)) {
108         return false;
109       }
110     }
111   }
112   return true;
113 }
114 
115 // handles null oops
116 static bool equal_oops(oop obj1, oop obj2) {
117   if (obj1 == obj2) {
118     return true;
119   }
120 
121   if (obj1 != nullptr && obj2 != nullptr && obj1->klass() == obj2->klass() && obj1->is_inline_type()) {
122     InlineKlass* vk = InlineKlass::cast(obj1->klass());
123     return equal_value_objects(obj1, 0, obj2, 0, vk);
124   }
125   return false;
126 }
127 
128 bool JvmtiHeapwalkObject::equals(const JvmtiHeapwalkObject& obj1, const JvmtiHeapwalkObject& obj2) {
129   if (obj1 == obj2) { // the same oop/offset/inline_klass
130     return true;
131   }
132 
133   if (obj1.is_value() && obj1.inline_klass() == obj2.inline_klass()) {
134     // instances of the same value class
135     return equal_value_objects(obj1.obj(), obj1.offset(), obj2.obj(), obj2.offset(), obj1.inline_klass());
136   }
137   return false;
138 }
139 
140 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiHeapwalkObject* obj) : _obj(obj) {
141 }
142 
143 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src): _h() {
144   if (src._obj != nullptr) {
145     // move object into Handle when copying into the table
146     assert(!src._obj->is_flat(), "cannot put flat object to JvmtiTagMapKey");
147     _is_weak = !src._obj->is_value();
148 
149     // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
150     // a heap walk.  The object needs to be kept alive when it is published.
151     Universe::heap()->keep_alive(src._obj->obj());
152 
153     if (_is_weak) {
154       _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj->obj());
155     } else {
156       _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._obj->obj());
157     }
158   } else {
159     // resizing needs to create a copy.
160     _is_weak = src._is_weak;
161     if (_is_weak) {
162       _wh = src._wh;
163     } else {
164       _h = src._h;
165     }
166   }
167   // obj is always null after a copy.
168   _obj = nullptr;
169 }
170 
171 void JvmtiTagMapKey::release_handle() {
172   if (_is_weak) {
173     _wh.release(JvmtiExport::weak_tag_storage());
174   } else {
175     _h.release(JvmtiExport::jvmti_oop_storage());
176   }
177 }
178 
179 JvmtiHeapwalkObject JvmtiTagMapKey::heapwalk_object() const {
180   if (_obj != nullptr) {
181     return JvmtiHeapwalkObject(_obj->obj(), _obj->offset(), _obj->inline_klass(), _obj->layout_kind());
182   }
183   oop obj = object_no_keepalive();
184   if (obj == nullptr) {
185     // The object was deleted by CG while tag still exists
186     return JvmtiHeapwalkObject();
187   }
188   return JvmtiHeapwalkObject(obj);
189 }
190 
191 oop JvmtiTagMapKey::object() const {
192   assert(_obj == nullptr, "Must have a handle and not object");
193   return _is_weak ? _wh.resolve() : _h.resolve();
194 }
195 
196 oop JvmtiTagMapKey::object_no_keepalive() const {
197   assert(_obj == nullptr, "Must have a handle and not object");
198   return _is_weak ? _wh.peek() : _h.peek();
199 }
200 
201 unsigned JvmtiTagMapKey::get_hash(const JvmtiTagMapKey& entry) {
202   const JvmtiHeapwalkObject* obj = entry._obj;
203   assert(obj != nullptr, "must lookup obj to hash");
204   if (obj->is_value()) {
205     return get_value_object_hash(*obj);
206   } else {
207     return (unsigned)obj->obj()->identity_hash();
208   }
209 }
210 
211 bool JvmtiTagMapKey::equals(const JvmtiTagMapKey& lhs, const JvmtiTagMapKey& rhs) {
212   JvmtiHeapwalkObject lhs_obj = lhs.heapwalk_object();
213   JvmtiHeapwalkObject rhs_obj = rhs.heapwalk_object();
214   return JvmtiHeapwalkObject::equals(lhs_obj, rhs_obj);
215 }
216 
217 static const int INITIAL_TABLE_SIZE = 1007;
218 static const int MAX_TABLE_SIZE     = 0x3fffffff;
219 
220 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
221 
222 void JvmtiTagMapTable::clear() {
223   struct RemoveAll {
224     bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
225       entry.release_handle();
226       return true;
227     }
228   } remove_all;
229   // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
230   // while iterating over all the elements of the table. If the do_entry() method returns true the element
231   // will be removed.
232   // In this case, we always return true from do_entry to clear all the elements.
233   _table.unlink(&remove_all);
234 
235   assert(_table.number_of_entries() == 0, "should have removed all entries");
236 }
237 
238 JvmtiTagMapTable::~JvmtiTagMapTable() {
239   clear();
240 }
241 
242 jlong* JvmtiTagMapTable::lookup(const JvmtiHeapwalkObject& obj) const {
243   if (is_empty()) {
244     return nullptr;
245   }
246 
247   if (!obj.is_value()) {
248     if (obj.obj()->fast_no_hash_check()) {
249       // Objects in the table all have a hashcode, unless inlined types.
250       return nullptr;
251     }
252   }
253   JvmtiTagMapKey entry(&obj);
254   jlong* found = _table.get(entry);
255   return found;
256 }
257 
258 
259 jlong JvmtiTagMapTable::find(const JvmtiHeapwalkObject& obj) const {
260   jlong* found = lookup(obj);
261   return found == nullptr ? 0 : *found;
262 }
263 
264 void JvmtiTagMapTable::add(const JvmtiHeapwalkObject& obj, jlong tag) {
265   assert(!obj.is_flat(), "Cannot add flat object to JvmtiTagMapTable");
266   JvmtiTagMapKey new_entry(&obj);
267   bool is_added;
268   if (!obj.is_value() && obj.obj()->fast_no_hash_check()) {
269     // Can't be in the table so add it fast.
270     is_added = _table.put_when_absent(new_entry, tag);
271   } else {
272     jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
273     *value = tag; // assign the new tag
274   }
275   if (is_added) {
276     if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
277       int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
278       log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
279                               _table.table_size(), _table.number_of_entries(), max_bucket_size);
280     }
281   }
282 }
283 
284 bool JvmtiTagMapTable::update(const JvmtiHeapwalkObject& obj, jlong tag) {
285   jlong* found = lookup(obj);
286   if (found == nullptr) {
287     return false;
288   }
289   *found = tag;
290   return true;
291 }
292 
293 bool JvmtiTagMapTable::remove(const JvmtiHeapwalkObject& obj) {
294   JvmtiTagMapKey entry(&obj);
295   auto clean = [](JvmtiTagMapKey & entry, jlong tag) {
296     entry.release_handle();
297   };
298   return _table.remove(entry, clean);
299 }
300 
301 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
302   _table.iterate(closure);
303 }
304 
305 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
306   struct IsDead {
307     GrowableArray<jlong>* _objects;
308     IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
309     bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
310       if (entry.object_no_keepalive() == nullptr) {
311         if (_objects != nullptr) {
312           _objects->append(tag);
313         }
314         entry.release_handle();
315         return true;
316       }
317       return false;;
318     }
319   } is_dead(objects);
320   _table.unlink(&is_dead);
321 }
322 
323 JvmtiFlatTagMapKey::JvmtiFlatTagMapKey(const JvmtiHeapwalkObject& obj)
324   : _holder(obj.obj()), _offset(obj.offset()), _inline_klass(obj.inline_klass()), _layout_kind(obj.layout_kind()) {
325 }
326 
327 JvmtiFlatTagMapKey::JvmtiFlatTagMapKey(const JvmtiFlatTagMapKey& src) : _h() {
328   // move object into Handle when copying into the table
329   if (src._holder != nullptr) {
330     // Holder object was read with AS_NO_KEEPALIVE. Needs to be kept alive when it is published.
331     Universe::heap()->keep_alive(src._holder);
332     _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._holder);
333   } else {
334     // resizing needs to create a copy.
335     _h = src._h;
336   }
337   // holder object is always null after a copy.
338   _holder = nullptr;
339   _offset = src._offset;
340   _inline_klass = src._inline_klass;
341   _layout_kind = src._layout_kind;
342 }
343 
344 JvmtiHeapwalkObject JvmtiFlatTagMapKey::heapwalk_object() const {
345   return JvmtiHeapwalkObject(_holder != nullptr ? _holder : holder_no_keepalive(), _offset, _inline_klass, _layout_kind);
346 }
347 
348 oop JvmtiFlatTagMapKey::holder() const {
349   assert(_holder == nullptr, "Must have a handle and not object");
350   return _h.resolve();
351 }
352 
353 oop JvmtiFlatTagMapKey::holder_no_keepalive() const {
354   assert(_holder == nullptr, "Must have a handle and not object");
355   return _h.peek();
356 
357 }
358 
359 void JvmtiFlatTagMapKey::release_handle() {
360   _h.release(JvmtiExport::jvmti_oop_storage());
361 }
362 
363 unsigned JvmtiFlatTagMapKey::get_hash(const JvmtiFlatTagMapKey& entry) {
364   return get_value_object_hash(entry._holder, entry._offset, entry._inline_klass);
365 }
366 
367 bool JvmtiFlatTagMapKey::equals(const JvmtiFlatTagMapKey& lhs, const JvmtiFlatTagMapKey& rhs) {
368   if (lhs._inline_klass == rhs._inline_klass) {
369     oop lhs_obj = lhs._holder != nullptr ? lhs._holder : lhs._h.peek();
370     oop rhs_obj = rhs._holder != nullptr ? rhs._holder : rhs._h.peek();
371     return equal_value_objects(lhs_obj, lhs._offset, rhs_obj, rhs._offset, lhs._inline_klass);
372   }
373   return false;
374 }
375 
376 JvmtiFlatTagMapTable::JvmtiFlatTagMapTable(): _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
377 
378 JvmtiFlatTagMapTable::~JvmtiFlatTagMapTable() {
379   clear();
380 }
381 
382 jlong JvmtiFlatTagMapTable::find(const JvmtiHeapwalkObject& obj) const {
383   if (is_empty()) {
384     return 0;
385   }
386 
387   JvmtiFlatTagMapKey entry(obj);
388   jlong* found = _table.get(entry);
389   return found == nullptr ? 0 : *found;
390 }
391 
392 void JvmtiFlatTagMapTable::add(const JvmtiHeapwalkObject& obj, jlong tag) {
393   assert(obj.is_value() && obj.is_flat(), "Must be flattened value object");
394   JvmtiFlatTagMapKey entry(obj);
395   bool is_added;
396   jlong* value = _table.put_if_absent(entry, tag, &is_added);
397   *value = tag; // assign the new tag
398   if (is_added) {
399     if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
400       int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
401       log_info(jvmti, table) ("JvmtiFlatTagMapTable table resized to %d for %d entries max bucket %d",
402         _table.table_size(), _table.number_of_entries(), max_bucket_size);
403     }
404   }
405 }
406 
407 jlong JvmtiFlatTagMapTable::remove(const JvmtiHeapwalkObject& obj) {
408   JvmtiFlatTagMapKey entry(obj);
409   jlong ret = 0;
410   auto clean = [&](JvmtiFlatTagMapKey& entry, jlong tag) {
411     ret = tag;
412     entry.release_handle();
413   };
414   _table.remove(entry, clean);
415   return ret;
416 }
417 
418 void JvmtiFlatTagMapTable::entry_iterate(JvmtiFlatTagMapKeyClosure* closure) {
419   _table.iterate(closure);
420 }
421 
422 void JvmtiFlatTagMapTable::clear() {
423   struct RemoveAll {
424     bool do_entry(JvmtiFlatTagMapKey& entry, const jlong& tag) {
425       entry.release_handle();
426       return true;
427     }
428   } remove_all;
429   // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
430   // while iterating over all the elements of the table. If the do_entry() method returns true the element
431   // will be removed.
432   // In this case, we always return true from do_entry to clear all the elements.
433   _table.unlink(&remove_all);
434 
435   assert(_table.number_of_entries() == 0, "should have removed all entries");
436 }
< prev index next >