Thu, 07 Feb 2013 16:05:48 -0500
Merge
duke@435 | 1 | /* |
acorn@4497 | 2 | * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. |
duke@435 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
duke@435 | 4 | * |
duke@435 | 5 | * This code is free software; you can redistribute it and/or modify it |
duke@435 | 6 | * under the terms of the GNU General Public License version 2 only, as |
duke@435 | 7 | * published by the Free Software Foundation. |
duke@435 | 8 | * |
duke@435 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
duke@435 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
duke@435 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
duke@435 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
duke@435 | 13 | * accompanied this code). |
duke@435 | 14 | * |
duke@435 | 15 | * You should have received a copy of the GNU General Public License version |
duke@435 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
duke@435 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
duke@435 | 18 | * |
trims@1907 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
trims@1907 | 20 | * or visit www.oracle.com if you need additional information or have any |
trims@1907 | 21 | * questions. |
duke@435 | 22 | * |
duke@435 | 23 | */ |
duke@435 | 24 | |
stefank@2314 | 25 | #include "precompiled.hpp" |
acorn@4497 | 26 | #include "classfile/classLoaderData.hpp" |
stefank@2314 | 27 | #include "gc_interface/collectedHeap.hpp" |
stefank@2314 | 28 | #include "memory/genCollectedHeap.hpp" |
stefank@2314 | 29 | #include "memory/heapInspection.hpp" |
stefank@2314 | 30 | #include "memory/resourceArea.hpp" |
stefank@2314 | 31 | #include "runtime/os.hpp" |
stefank@2314 | 32 | #include "utilities/globalDefinitions.hpp" |
jprovino@4542 | 33 | #include "utilities/macros.hpp" |
jprovino@4542 | 34 | #if INCLUDE_ALL_GCS |
stefank@2314 | 35 | #include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" |
jprovino@4542 | 36 | #endif // INCLUDE_ALL_GCS |
duke@435 | 37 | |
duke@435 | 38 | // HeapInspection |
duke@435 | 39 | |
duke@435 | 40 | int KlassInfoEntry::compare(KlassInfoEntry* e1, KlassInfoEntry* e2) { |
duke@435 | 41 | if(e1->_instance_words > e2->_instance_words) { |
duke@435 | 42 | return -1; |
duke@435 | 43 | } else if(e1->_instance_words < e2->_instance_words) { |
duke@435 | 44 | return 1; |
duke@435 | 45 | } |
acorn@4497 | 46 | // Sort alphabetically, note 'Z' < '[' < 'a', but it's better to group |
acorn@4497 | 47 | // the array classes before all the instance classes. |
acorn@4497 | 48 | ResourceMark rm; |
acorn@4497 | 49 | const char* name1 = e1->klass()->external_name(); |
acorn@4497 | 50 | const char* name2 = e2->klass()->external_name(); |
acorn@4497 | 51 | bool d1 = (name1[0] == '['); |
acorn@4497 | 52 | bool d2 = (name2[0] == '['); |
acorn@4497 | 53 | if (d1 && !d2) { |
acorn@4497 | 54 | return -1; |
acorn@4497 | 55 | } else if (d2 && !d1) { |
acorn@4497 | 56 | return 1; |
acorn@4497 | 57 | } else { |
acorn@4497 | 58 | return strcmp(name1, name2); |
acorn@4497 | 59 | } |
duke@435 | 60 | } |
duke@435 | 61 | |
acorn@4497 | 62 | const char* KlassInfoEntry::name() const { |
acorn@4497 | 63 | const char* name; |
coleenp@4037 | 64 | if (_klass->name() != NULL) { |
coleenp@4037 | 65 | name = _klass->external_name(); |
duke@435 | 66 | } else { |
duke@435 | 67 | if (_klass == Universe::boolArrayKlassObj()) name = "<boolArrayKlass>"; else |
duke@435 | 68 | if (_klass == Universe::charArrayKlassObj()) name = "<charArrayKlass>"; else |
duke@435 | 69 | if (_klass == Universe::singleArrayKlassObj()) name = "<singleArrayKlass>"; else |
duke@435 | 70 | if (_klass == Universe::doubleArrayKlassObj()) name = "<doubleArrayKlass>"; else |
duke@435 | 71 | if (_klass == Universe::byteArrayKlassObj()) name = "<byteArrayKlass>"; else |
duke@435 | 72 | if (_klass == Universe::shortArrayKlassObj()) name = "<shortArrayKlass>"; else |
duke@435 | 73 | if (_klass == Universe::intArrayKlassObj()) name = "<intArrayKlass>"; else |
duke@435 | 74 | if (_klass == Universe::longArrayKlassObj()) name = "<longArrayKlass>"; else |
duke@435 | 75 | name = "<no name>"; |
duke@435 | 76 | } |
acorn@4497 | 77 | return name; |
acorn@4497 | 78 | } |
acorn@4497 | 79 | |
acorn@4497 | 80 | void KlassInfoEntry::print_on(outputStream* st) const { |
acorn@4497 | 81 | ResourceMark rm; |
acorn@4497 | 82 | |
duke@435 | 83 | // simplify the formatting (ILP32 vs LP64) - always cast the numbers to 64-bit |
ysr@446 | 84 | st->print_cr(INT64_FORMAT_W(13) " " UINT64_FORMAT_W(13) " %s", |
duke@435 | 85 | (jlong) _instance_count, |
duke@435 | 86 | (julong) _instance_words * HeapWordSize, |
acorn@4497 | 87 | name()); |
duke@435 | 88 | } |
duke@435 | 89 | |
coleenp@4037 | 90 | KlassInfoEntry* KlassInfoBucket::lookup(Klass* const k) { |
duke@435 | 91 | KlassInfoEntry* elt = _list; |
duke@435 | 92 | while (elt != NULL) { |
duke@435 | 93 | if (elt->is_equal(k)) { |
duke@435 | 94 | return elt; |
duke@435 | 95 | } |
duke@435 | 96 | elt = elt->next(); |
duke@435 | 97 | } |
duke@435 | 98 | elt = new KlassInfoEntry(k, list()); |
ysr@446 | 99 | // We may be out of space to allocate the new entry. |
ysr@446 | 100 | if (elt != NULL) { |
ysr@446 | 101 | set_list(elt); |
ysr@446 | 102 | } |
duke@435 | 103 | return elt; |
duke@435 | 104 | } |
duke@435 | 105 | |
duke@435 | 106 | void KlassInfoBucket::iterate(KlassInfoClosure* cic) { |
duke@435 | 107 | KlassInfoEntry* elt = _list; |
duke@435 | 108 | while (elt != NULL) { |
duke@435 | 109 | cic->do_cinfo(elt); |
duke@435 | 110 | elt = elt->next(); |
duke@435 | 111 | } |
duke@435 | 112 | } |
duke@435 | 113 | |
duke@435 | 114 | void KlassInfoBucket::empty() { |
duke@435 | 115 | KlassInfoEntry* elt = _list; |
duke@435 | 116 | _list = NULL; |
duke@435 | 117 | while (elt != NULL) { |
duke@435 | 118 | KlassInfoEntry* next = elt->next(); |
duke@435 | 119 | delete elt; |
duke@435 | 120 | elt = next; |
duke@435 | 121 | } |
duke@435 | 122 | } |
duke@435 | 123 | |
acorn@4497 | 124 | void KlassInfoTable::AllClassesFinder::do_klass(Klass* k) { |
acorn@4497 | 125 | // This has the SIDE EFFECT of creating a KlassInfoEntry |
acorn@4497 | 126 | // for <k>, if one doesn't exist yet. |
acorn@4497 | 127 | _table->lookup(k); |
acorn@4497 | 128 | } |
acorn@4497 | 129 | |
acorn@4497 | 130 | KlassInfoTable::KlassInfoTable(int size, HeapWord* ref, |
acorn@4497 | 131 | bool need_class_stats) { |
ysr@446 | 132 | _size = 0; |
duke@435 | 133 | _ref = ref; |
zgu@3900 | 134 | _buckets = NEW_C_HEAP_ARRAY(KlassInfoBucket, size, mtInternal); |
ysr@446 | 135 | if (_buckets != NULL) { |
ysr@446 | 136 | _size = size; |
ysr@446 | 137 | for (int index = 0; index < _size; index++) { |
ysr@446 | 138 | _buckets[index].initialize(); |
ysr@446 | 139 | } |
acorn@4497 | 140 | if (need_class_stats) { |
acorn@4497 | 141 | AllClassesFinder finder(this); |
acorn@4497 | 142 | ClassLoaderDataGraph::classes_do(&finder); |
acorn@4497 | 143 | } |
duke@435 | 144 | } |
duke@435 | 145 | } |
duke@435 | 146 | |
duke@435 | 147 | KlassInfoTable::~KlassInfoTable() { |
ysr@446 | 148 | if (_buckets != NULL) { |
ysr@446 | 149 | for (int index = 0; index < _size; index++) { |
ysr@446 | 150 | _buckets[index].empty(); |
ysr@446 | 151 | } |
zgu@3900 | 152 | FREE_C_HEAP_ARRAY(KlassInfoBucket, _buckets, mtInternal); |
ysr@446 | 153 | _size = 0; |
duke@435 | 154 | } |
duke@435 | 155 | } |
duke@435 | 156 | |
coleenp@4037 | 157 | uint KlassInfoTable::hash(Klass* p) { |
coleenp@4037 | 158 | assert(p->is_metadata(), "all klasses are metadata"); |
duke@435 | 159 | return (uint)(((uintptr_t)p - (uintptr_t)_ref) >> 2); |
duke@435 | 160 | } |
duke@435 | 161 | |
coleenp@4037 | 162 | KlassInfoEntry* KlassInfoTable::lookup(Klass* const k) { |
duke@435 | 163 | uint idx = hash(k) % _size; |
ysr@446 | 164 | assert(_buckets != NULL, "Allocation failure should have been caught"); |
duke@435 | 165 | KlassInfoEntry* e = _buckets[idx].lookup(k); |
ysr@446 | 166 | // Lookup may fail if this is a new klass for which we |
ysr@446 | 167 | // could not allocate space for an new entry. |
ysr@446 | 168 | assert(e == NULL || k == e->klass(), "must be equal"); |
duke@435 | 169 | return e; |
duke@435 | 170 | } |
duke@435 | 171 | |
ysr@446 | 172 | // Return false if the entry could not be recorded on account |
ysr@446 | 173 | // of running out of space required to create a new entry. |
ysr@446 | 174 | bool KlassInfoTable::record_instance(const oop obj) { |
coleenp@4037 | 175 | Klass* k = obj->klass(); |
duke@435 | 176 | KlassInfoEntry* elt = lookup(k); |
ysr@446 | 177 | // elt may be NULL if it's a new klass for which we |
ysr@446 | 178 | // could not allocate space for a new entry in the hashtable. |
ysr@446 | 179 | if (elt != NULL) { |
ysr@446 | 180 | elt->set_count(elt->count() + 1); |
ysr@446 | 181 | elt->set_words(elt->words() + obj->size()); |
ysr@446 | 182 | return true; |
ysr@446 | 183 | } else { |
ysr@446 | 184 | return false; |
ysr@446 | 185 | } |
duke@435 | 186 | } |
duke@435 | 187 | |
duke@435 | 188 | void KlassInfoTable::iterate(KlassInfoClosure* cic) { |
ysr@446 | 189 | assert(_size == 0 || _buckets != NULL, "Allocation failure should have been caught"); |
duke@435 | 190 | for (int index = 0; index < _size; index++) { |
duke@435 | 191 | _buckets[index].iterate(cic); |
duke@435 | 192 | } |
duke@435 | 193 | } |
duke@435 | 194 | |
duke@435 | 195 | int KlassInfoHisto::sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2) { |
duke@435 | 196 | return (*e1)->compare(*e1,*e2); |
duke@435 | 197 | } |
duke@435 | 198 | |
acorn@4497 | 199 | KlassInfoHisto::KlassInfoHisto(KlassInfoTable* cit, const char* title, int estimatedCount) : |
acorn@4497 | 200 | _cit(cit), |
duke@435 | 201 | _title(title) { |
zgu@3900 | 202 | _elements = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<KlassInfoEntry*>(estimatedCount,true); |
duke@435 | 203 | } |
duke@435 | 204 | |
duke@435 | 205 | KlassInfoHisto::~KlassInfoHisto() { |
duke@435 | 206 | delete _elements; |
duke@435 | 207 | } |
duke@435 | 208 | |
duke@435 | 209 | void KlassInfoHisto::add(KlassInfoEntry* cie) { |
duke@435 | 210 | elements()->append(cie); |
duke@435 | 211 | } |
duke@435 | 212 | |
duke@435 | 213 | void KlassInfoHisto::sort() { |
duke@435 | 214 | elements()->sort(KlassInfoHisto::sort_helper); |
duke@435 | 215 | } |
duke@435 | 216 | |
duke@435 | 217 | void KlassInfoHisto::print_elements(outputStream* st) const { |
duke@435 | 218 | // simplify the formatting (ILP32 vs LP64) - store the sum in 64-bit |
duke@435 | 219 | jlong total = 0; |
duke@435 | 220 | julong totalw = 0; |
duke@435 | 221 | for(int i=0; i < elements()->length(); i++) { |
duke@435 | 222 | st->print("%4d: ", i+1); |
duke@435 | 223 | elements()->at(i)->print_on(st); |
duke@435 | 224 | total += elements()->at(i)->count(); |
duke@435 | 225 | totalw += elements()->at(i)->words(); |
duke@435 | 226 | } |
ysr@446 | 227 | st->print_cr("Total " INT64_FORMAT_W(13) " " UINT64_FORMAT_W(13), |
duke@435 | 228 | total, totalw * HeapWordSize); |
duke@435 | 229 | } |
duke@435 | 230 | |
acorn@4497 | 231 | #define MAKE_COL_NAME(field, name, help) #name, |
acorn@4497 | 232 | #define MAKE_COL_HELP(field, name, help) help, |
acorn@4497 | 233 | |
acorn@4497 | 234 | static const char *name_table[] = { |
acorn@4497 | 235 | HEAP_INSPECTION_COLUMNS_DO(MAKE_COL_NAME) |
acorn@4497 | 236 | }; |
acorn@4497 | 237 | |
acorn@4497 | 238 | static const char *help_table[] = { |
acorn@4497 | 239 | HEAP_INSPECTION_COLUMNS_DO(MAKE_COL_HELP) |
acorn@4497 | 240 | }; |
acorn@4497 | 241 | |
acorn@4497 | 242 | bool KlassInfoHisto::is_selected(const char *col_name) { |
acorn@4497 | 243 | if (_selected_columns == NULL) { |
acorn@4497 | 244 | return true; |
acorn@4497 | 245 | } |
acorn@4497 | 246 | if (strcmp(_selected_columns, col_name) == 0) { |
acorn@4497 | 247 | return true; |
acorn@4497 | 248 | } |
acorn@4497 | 249 | |
acorn@4497 | 250 | const char *start = strstr(_selected_columns, col_name); |
acorn@4497 | 251 | if (start == NULL) { |
acorn@4497 | 252 | return false; |
acorn@4497 | 253 | } |
acorn@4497 | 254 | |
acorn@4497 | 255 | // The following must be true, because _selected_columns != col_name |
acorn@4497 | 256 | if (start > _selected_columns && start[-1] != ',') { |
acorn@4497 | 257 | return false; |
acorn@4497 | 258 | } |
acorn@4497 | 259 | char x = start[strlen(col_name)]; |
acorn@4497 | 260 | if (x != ',' && x != '\0') { |
acorn@4497 | 261 | return false; |
acorn@4497 | 262 | } |
acorn@4497 | 263 | |
acorn@4497 | 264 | return true; |
acorn@4497 | 265 | } |
acorn@4497 | 266 | |
acorn@4497 | 267 | void KlassInfoHisto::print_title(outputStream* st, bool csv_format, |
acorn@4497 | 268 | bool selected[], int width_table[], |
acorn@4497 | 269 | const char *name_table[]) { |
acorn@4497 | 270 | if (csv_format) { |
acorn@4497 | 271 | st->print("Index,Super"); |
acorn@4497 | 272 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 273 | if (selected[c]) {st->print(",%s", name_table[c]);} |
acorn@4497 | 274 | } |
acorn@4497 | 275 | st->print(",ClassName"); |
acorn@4497 | 276 | } else { |
acorn@4497 | 277 | st->print("Index Super"); |
acorn@4497 | 278 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 279 | if (selected[c]) {st->print(str_fmt(width_table[c]), name_table[c]);} |
acorn@4497 | 280 | } |
acorn@4497 | 281 | st->print(" ClassName"); |
acorn@4497 | 282 | } |
acorn@4497 | 283 | |
acorn@4497 | 284 | if (is_selected("ClassLoader")) { |
acorn@4497 | 285 | st->print(",ClassLoader"); |
acorn@4497 | 286 | } |
acorn@4497 | 287 | st->cr(); |
acorn@4497 | 288 | } |
acorn@4497 | 289 | |
acorn@4497 | 290 | void KlassInfoHisto::print_class_stats(outputStream* st, |
acorn@4497 | 291 | bool csv_format, const char *columns) { |
acorn@4497 | 292 | ResourceMark rm; |
acorn@4497 | 293 | KlassSizeStats sz, sz_sum; |
acorn@4497 | 294 | int i; |
acorn@4497 | 295 | julong *col_table = (julong*)(&sz); |
acorn@4497 | 296 | julong *colsum_table = (julong*)(&sz_sum); |
acorn@4497 | 297 | int width_table[KlassSizeStats::_num_columns]; |
acorn@4497 | 298 | bool selected[KlassSizeStats::_num_columns]; |
acorn@4497 | 299 | |
acorn@4497 | 300 | _selected_columns = columns; |
acorn@4497 | 301 | |
acorn@4497 | 302 | memset(&sz_sum, 0, sizeof(sz_sum)); |
acorn@4497 | 303 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 304 | selected[c] = is_selected(name_table[c]); |
acorn@4497 | 305 | } |
acorn@4497 | 306 | |
acorn@4497 | 307 | for(i=0; i < elements()->length(); i++) { |
acorn@4497 | 308 | elements()->at(i)->set_index(i+1); |
acorn@4497 | 309 | } |
acorn@4497 | 310 | |
acorn@4497 | 311 | for (int pass=1; pass<=2; pass++) { |
acorn@4497 | 312 | if (pass == 2) { |
acorn@4497 | 313 | print_title(st, csv_format, selected, width_table, name_table); |
acorn@4497 | 314 | } |
acorn@4497 | 315 | for(i=0; i < elements()->length(); i++) { |
acorn@4497 | 316 | KlassInfoEntry* e = (KlassInfoEntry*)elements()->at(i); |
acorn@4497 | 317 | const Klass* k = e->klass(); |
acorn@4497 | 318 | |
acorn@4497 | 319 | memset(&sz, 0, sizeof(sz)); |
acorn@4497 | 320 | sz._inst_count = e->count(); |
acorn@4497 | 321 | sz._inst_bytes = HeapWordSize * e->words(); |
acorn@4497 | 322 | k->collect_statistics(&sz); |
acorn@4497 | 323 | sz._total_bytes = sz._ro_bytes + sz._rw_bytes; |
acorn@4497 | 324 | |
acorn@4497 | 325 | if (pass == 1) { |
acorn@4497 | 326 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 327 | colsum_table[c] += col_table[c]; |
acorn@4497 | 328 | } |
acorn@4497 | 329 | } else { |
acorn@4497 | 330 | int super_index = -1; |
acorn@4497 | 331 | if (k->oop_is_instance()) { |
acorn@4497 | 332 | Klass* super = ((InstanceKlass*)k)->java_super(); |
acorn@4497 | 333 | if (super) { |
acorn@4497 | 334 | KlassInfoEntry* super_e = _cit->lookup(super); |
acorn@4497 | 335 | if (super_e) { |
acorn@4497 | 336 | super_index = super_e->index(); |
acorn@4497 | 337 | } |
acorn@4497 | 338 | } |
acorn@4497 | 339 | } |
acorn@4497 | 340 | |
acorn@4497 | 341 | if (csv_format) { |
acorn@4497 | 342 | st->print("%d,%d", e->index(), super_index); |
acorn@4497 | 343 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 344 | if (selected[c]) {st->print("," JULONG_FORMAT, col_table[c]);} |
acorn@4497 | 345 | } |
acorn@4497 | 346 | st->print(",%s",e->name()); |
acorn@4497 | 347 | } else { |
acorn@4497 | 348 | st->print("%5d %5d", e->index(), super_index); |
acorn@4497 | 349 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 350 | if (selected[c]) {print_julong(st, width_table[c], col_table[c]);} |
acorn@4497 | 351 | } |
acorn@4497 | 352 | st->print(" %s", e->name()); |
acorn@4497 | 353 | } |
acorn@4497 | 354 | if (is_selected("ClassLoader")) { |
acorn@4497 | 355 | ClassLoaderData* loader_data = k->class_loader_data(); |
acorn@4497 | 356 | st->print(","); |
acorn@4497 | 357 | loader_data->print_value_on(st); |
acorn@4497 | 358 | } |
acorn@4497 | 359 | st->cr(); |
acorn@4497 | 360 | } |
acorn@4497 | 361 | } |
acorn@4497 | 362 | |
acorn@4497 | 363 | if (pass == 1) { |
acorn@4497 | 364 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 365 | width_table[c] = col_width(colsum_table[c], name_table[c]); |
acorn@4497 | 366 | } |
acorn@4497 | 367 | } |
acorn@4497 | 368 | } |
acorn@4497 | 369 | |
acorn@4497 | 370 | sz_sum._inst_size = 0; |
acorn@4497 | 371 | |
acorn@4497 | 372 | if (csv_format) { |
acorn@4497 | 373 | st->print(","); |
acorn@4497 | 374 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 375 | if (selected[c]) {st->print("," JULONG_FORMAT, colsum_table[c]);} |
acorn@4497 | 376 | } |
acorn@4497 | 377 | } else { |
acorn@4497 | 378 | st->print(" "); |
acorn@4497 | 379 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 380 | if (selected[c]) {print_julong(st, width_table[c], colsum_table[c]);} |
acorn@4497 | 381 | } |
acorn@4497 | 382 | st->print(" Total"); |
acorn@4497 | 383 | if (sz_sum._total_bytes > 0) { |
acorn@4497 | 384 | st->cr(); |
acorn@4497 | 385 | st->print(" "); |
acorn@4497 | 386 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 387 | if (selected[c]) { |
acorn@4497 | 388 | switch (c) { |
acorn@4497 | 389 | case KlassSizeStats::_index_inst_size: |
acorn@4497 | 390 | case KlassSizeStats::_index_inst_count: |
acorn@4497 | 391 | case KlassSizeStats::_index_method_count: |
acorn@4497 | 392 | st->print(str_fmt(width_table[c]), "-"); |
acorn@4497 | 393 | break; |
acorn@4497 | 394 | default: |
acorn@4497 | 395 | { |
acorn@4497 | 396 | double perc = (double)(100) * (double)(colsum_table[c]) / (double)sz_sum._total_bytes; |
acorn@4497 | 397 | st->print(perc_fmt(width_table[c]), perc); |
acorn@4497 | 398 | } |
acorn@4497 | 399 | } |
acorn@4497 | 400 | } |
acorn@4497 | 401 | } |
acorn@4497 | 402 | } |
acorn@4497 | 403 | } |
acorn@4497 | 404 | st->cr(); |
acorn@4497 | 405 | |
acorn@4497 | 406 | if (!csv_format) { |
acorn@4497 | 407 | print_title(st, csv_format, selected, width_table, name_table); |
acorn@4497 | 408 | } |
acorn@4497 | 409 | } |
acorn@4497 | 410 | |
acorn@4497 | 411 | julong KlassInfoHisto::annotations_bytes(Array<AnnotationArray*>* p) const { |
acorn@4497 | 412 | julong bytes = 0; |
acorn@4497 | 413 | if (p != NULL) { |
acorn@4497 | 414 | for (int i = 0; i < p->length(); i++) { |
acorn@4497 | 415 | bytes += count_bytes_array(p->at(i)); |
acorn@4497 | 416 | } |
acorn@4497 | 417 | bytes += count_bytes_array(p); |
acorn@4497 | 418 | } |
acorn@4497 | 419 | return bytes; |
acorn@4497 | 420 | } |
acorn@4497 | 421 | |
acorn@4497 | 422 | void KlassInfoHisto::print_histo_on(outputStream* st, bool print_stats, |
acorn@4497 | 423 | bool csv_format, const char *columns) { |
acorn@4497 | 424 | if (print_stats) { |
acorn@4497 | 425 | print_class_stats(st, csv_format, columns); |
acorn@4497 | 426 | } else { |
acorn@4497 | 427 | st->print_cr("%s",title()); |
acorn@4497 | 428 | print_elements(st); |
acorn@4497 | 429 | } |
duke@435 | 430 | } |
duke@435 | 431 | |
duke@435 | 432 | class HistoClosure : public KlassInfoClosure { |
duke@435 | 433 | private: |
duke@435 | 434 | KlassInfoHisto* _cih; |
duke@435 | 435 | public: |
duke@435 | 436 | HistoClosure(KlassInfoHisto* cih) : _cih(cih) {} |
duke@435 | 437 | |
duke@435 | 438 | void do_cinfo(KlassInfoEntry* cie) { |
duke@435 | 439 | _cih->add(cie); |
duke@435 | 440 | } |
duke@435 | 441 | }; |
duke@435 | 442 | |
duke@435 | 443 | class RecordInstanceClosure : public ObjectClosure { |
duke@435 | 444 | private: |
duke@435 | 445 | KlassInfoTable* _cit; |
ysr@446 | 446 | size_t _missed_count; |
duke@435 | 447 | public: |
ysr@446 | 448 | RecordInstanceClosure(KlassInfoTable* cit) : |
ysr@446 | 449 | _cit(cit), _missed_count(0) {} |
duke@435 | 450 | |
duke@435 | 451 | void do_object(oop obj) { |
ysr@446 | 452 | if (!_cit->record_instance(obj)) { |
ysr@446 | 453 | _missed_count++; |
ysr@446 | 454 | } |
duke@435 | 455 | } |
ysr@446 | 456 | |
ysr@446 | 457 | size_t missed_count() { return _missed_count; } |
duke@435 | 458 | }; |
duke@435 | 459 | |
ysr@1050 | 460 | void HeapInspection::heap_inspection(outputStream* st, bool need_prologue) { |
duke@435 | 461 | ResourceMark rm; |
coleenp@4037 | 462 | // Get some random number for ref (the hash key) |
coleenp@4037 | 463 | HeapWord* ref = (HeapWord*) Universe::boolArrayKlassObj(); |
duke@435 | 464 | CollectedHeap* heap = Universe::heap(); |
ysr@777 | 465 | bool is_shared_heap = false; |
coleenp@4037 | 466 | |
acorn@4497 | 467 | if (_print_help) { |
acorn@4497 | 468 | for (int c=0; c<KlassSizeStats::_num_columns; c++) { |
acorn@4497 | 469 | st->print("%s:\n\t", name_table[c]); |
acorn@4497 | 470 | const int max_col = 60; |
acorn@4497 | 471 | int col = 0; |
acorn@4497 | 472 | for (const char *p = help_table[c]; *p; p++,col++) { |
acorn@4497 | 473 | if (col >= max_col && *p == ' ') { |
acorn@4497 | 474 | st->print("\n\t"); |
acorn@4497 | 475 | col = 0; |
acorn@4497 | 476 | } else { |
acorn@4497 | 477 | st->print("%c", *p); |
acorn@4497 | 478 | } |
acorn@4497 | 479 | } |
acorn@4497 | 480 | st->print_cr(".\n"); |
acorn@4497 | 481 | } |
acorn@4497 | 482 | return; |
acorn@4497 | 483 | } |
acorn@4497 | 484 | |
duke@435 | 485 | // Collect klass instance info |
acorn@4497 | 486 | KlassInfoTable cit(KlassInfoTable::cit_size, ref, _print_class_stats); |
ysr@446 | 487 | if (!cit.allocation_failed()) { |
ysr@446 | 488 | // Iterate over objects in the heap |
ysr@446 | 489 | RecordInstanceClosure ric(&cit); |
ysr@446 | 490 | Universe::heap()->object_iterate(&ric); |
duke@435 | 491 | |
ysr@446 | 492 | // Report if certain classes are not counted because of |
ysr@446 | 493 | // running out of C-heap for the histogram. |
ysr@446 | 494 | size_t missed_count = ric.missed_count(); |
ysr@446 | 495 | if (missed_count != 0) { |
ysr@446 | 496 | st->print_cr("WARNING: Ran out of C-heap; undercounted " SIZE_FORMAT |
ysr@446 | 497 | " total instances in data below", |
ysr@446 | 498 | missed_count); |
ysr@446 | 499 | } |
ysr@446 | 500 | // Sort and print klass instance info |
acorn@4497 | 501 | const char *title = "\n" |
acorn@4497 | 502 | " num #instances #bytes class name\n" |
acorn@4497 | 503 | "----------------------------------------------"; |
acorn@4497 | 504 | KlassInfoHisto histo(&cit, title, KlassInfoHisto::histo_initial_size); |
ysr@446 | 505 | HistoClosure hc(&histo); |
ysr@446 | 506 | cit.iterate(&hc); |
ysr@446 | 507 | histo.sort(); |
acorn@4497 | 508 | histo.print_histo_on(st, _print_class_stats, _csv_format, _columns); |
ysr@446 | 509 | } else { |
ysr@446 | 510 | st->print_cr("WARNING: Ran out of C-heap; histogram not generated"); |
ysr@446 | 511 | } |
duke@435 | 512 | st->flush(); |
duke@435 | 513 | |
ysr@1050 | 514 | if (need_prologue && is_shared_heap) { |
ysr@777 | 515 | SharedHeap* sh = (SharedHeap*)heap; |
ysr@777 | 516 | sh->gc_epilogue(false /* !full */); // release all acquired locks, etc. |
duke@435 | 517 | } |
duke@435 | 518 | } |
duke@435 | 519 | |
duke@435 | 520 | class FindInstanceClosure : public ObjectClosure { |
duke@435 | 521 | private: |
coleenp@4037 | 522 | Klass* _klass; |
duke@435 | 523 | GrowableArray<oop>* _result; |
duke@435 | 524 | |
duke@435 | 525 | public: |
coleenp@4037 | 526 | FindInstanceClosure(Klass* k, GrowableArray<oop>* result) : _klass(k), _result(result) {}; |
duke@435 | 527 | |
duke@435 | 528 | void do_object(oop obj) { |
duke@435 | 529 | if (obj->is_a(_klass)) { |
duke@435 | 530 | _result->append(obj); |
duke@435 | 531 | } |
duke@435 | 532 | } |
duke@435 | 533 | }; |
duke@435 | 534 | |
coleenp@4037 | 535 | void HeapInspection::find_instances_at_safepoint(Klass* k, GrowableArray<oop>* result) { |
duke@435 | 536 | assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); |
jcoomes@1844 | 537 | assert(Heap_lock->is_locked(), "should have the Heap_lock"); |
duke@435 | 538 | |
duke@435 | 539 | // Ensure that the heap is parsable |
duke@435 | 540 | Universe::heap()->ensure_parsability(false); // no need to retire TALBs |
duke@435 | 541 | |
duke@435 | 542 | // Iterate over objects in the heap |
duke@435 | 543 | FindInstanceClosure fic(k, result); |
jmasa@952 | 544 | // If this operation encounters a bad object when using CMS, |
coleenp@4037 | 545 | // consider using safe_object_iterate() which avoids metadata |
jmasa@952 | 546 | // objects that may contain bad references. |
duke@435 | 547 | Universe::heap()->object_iterate(&fic); |
duke@435 | 548 | } |