--- old/src/hotspot/share/gc/shared/gcVMOperations.hpp 2020-07-09 14:50:44.144351924 +0800 +++ new/src/hotspot/share/gc/shared/gcVMOperations.hpp 2020-07-09 14:50:43.992352408 +0800 @@ -125,10 +125,10 @@ private: outputStream* _out; bool _full_gc; - size_t _parallel_thread_num; + uint _parallel_thread_num; public: VM_GC_HeapInspection(outputStream* out, bool request_full_gc, - size_t parallel_thread_num = 1) : + uint parallel_thread_num = 1) : VM_GC_Operation(0 /* total collections, dummy, ignored */, GCCause::_heap_inspection /* GC Cause */, 0 /* total full collections, dummy, ignored */, --- old/src/hotspot/share/memory/heapInspection.cpp 2020-07-09 14:50:44.680350223 +0800 +++ new/src/hotspot/share/memory/heapInspection.cpp 2020-07-09 14:50:44.524350718 +0800 @@ -241,7 +241,7 @@ // Return false if the entry could not be recorded on account // of running out of space required to create a new entry. bool KlassInfoTable::merge_entry(const KlassInfoEntry* cie) { - Klass* k = cie->klass(); + Klass* k = cie->klass(); KlassInfoEntry* elt = lookup(k); // elt may be NULL if it's a new klass for which we // could not allocate space for a new entry in the hashtable. @@ -250,9 +250,9 @@ elt->set_words(elt->words() + cie->words()); _size_of_instances_in_words += cie->words(); return true; - } else { - return false; } + + return false; } class KlassInfoTableMergeClosure : public KlassInfoClosure { @@ -264,14 +264,14 @@ void do_cinfo(KlassInfoEntry* cie) { _success &= _dest->merge_entry(cie); } - bool is_success() { return _success; } + bool success() { return _success; } }; // merge from table bool KlassInfoTable::merge(KlassInfoTable* table) { KlassInfoTableMergeClosure closure(this); table->iterate(&closure); - return closure.is_success(); + return closure.success(); } int KlassInfoHisto::sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2) { @@ -519,7 +519,7 @@ class RecordInstanceClosure : public ObjectClosure { private: KlassInfoTable* _cit; - size_t _missed_count; + uint _missed_count; BoolObjectClosure* _filter; public: RecordInstanceClosure(KlassInfoTable* cit, BoolObjectClosure* filter) : @@ -533,7 +533,7 @@ } } - size_t missed_count() { return _missed_count; } + uint missed_count() { return _missed_count; } private: bool should_visit(oop obj) { @@ -544,7 +544,7 @@ // Heap inspection for every worker. // When native OOM hanppens for KlassInfoTable, set _success to false. void ParHeapInspectTask::work(uint worker_id) { - size_t missed_count = 0; + uint missed_count = 0; bool merge_success = true; if (!Atomic::load(&_success)) { // other worker has failed on parallel iteration. @@ -552,31 +552,31 @@ } KlassInfoTable cit(false); - if (!cit.allocation_failed()) { - RecordInstanceClosure ric(&cit, _filter); - _poi->object_iterate(&ric, worker_id); - missed_count = ric.missed_count(); - } else { + if (cit.allocation_failed()) { // fail to allocate memory, stop parallel mode Atomic::store(&_success, false); return; } + RecordInstanceClosure ric(&cit, _filter); + _poi->object_iterate(&ric, worker_id); + missed_count = ric.missed_count(); { MutexLocker x(&_mutex); merge_success = _shared_cit->merge(&cit); } - if (!merge_success) { + if (merge_success) { + Atomic::add(&_missed_count, missed_count); + } else { Atomic::store(&_success, false); return; } - Atomic::add(&_shared_missed_count, missed_count); } -size_t HeapInspection::populate_table(KlassInfoTable* cit, BoolObjectClosure *filter, size_t parallel_thread_num) { - ResourceMark rm; +size_t HeapInspection::populate_table(KlassInfoTable* cit, BoolObjectClosure *filter, uint parallel_thread_num) { // Try parallel first. if (parallel_thread_num > 1) { + ResourceMark rm; ParallelObjectIterator* poi = Universe::heap()->parallel_object_iterator(parallel_thread_num); if (poi != NULL) { ParHeapInspectTask task(poi, cit, filter); @@ -588,13 +588,14 @@ } } + ResourceMark rm; // If no parallel iteration available, run serially. RecordInstanceClosure ric(cit, filter); Universe::heap()->object_iterate(&ric); return ric.missed_count(); } -void HeapInspection::heap_inspection(outputStream* st, size_t parallel_thread_num) { +void HeapInspection::heap_inspection(outputStream* st, uint parallel_thread_num) { ResourceMark rm; KlassInfoTable cit(false); --- old/src/hotspot/share/memory/heapInspection.hpp 2020-07-09 14:50:45.232348470 +0800 +++ new/src/hotspot/share/memory/heapInspection.hpp 2020-07-09 14:50:45.072348977 +0800 @@ -216,23 +216,22 @@ class HeapInspection : public StackObj { public: - void heap_inspection(outputStream* st, size_t parallel_thread_num = 1) NOT_SERVICES_RETURN; - size_t populate_table(KlassInfoTable* cit, BoolObjectClosure* filter = NULL, size_t parallel_thread_num = 1) NOT_SERVICES_RETURN_(0); + void heap_inspection(outputStream* st, uint parallel_thread_num = 1) NOT_SERVICES_RETURN; + size_t populate_table(KlassInfoTable* cit, BoolObjectClosure* filter = NULL, uint parallel_thread_num = 1) NOT_SERVICES_RETURN_(0); static void find_instances_at_safepoint(Klass* k, GrowableArray* result) NOT_SERVICES_RETURN; private: void iterate_over_heap(KlassInfoTable* cit, BoolObjectClosure* filter = NULL); }; -// Task for parallel heap inspection. The parallel inspection can be fail -// because of native OOM when allocation memory for TL-KlassInfoTable, it -// will set success to false when OOM so serial inspection can be tried. -// see work() implementation at heapInspection.cpp for more info. +// Parallel heap inspection task. Parallel inspection can fail due to +// a native OOM when allocating memory for TL-KlassInfoTable. +// _success will be set false on an OOM, and serial inspection tried. class ParHeapInspectTask : public AbstractGangTask { private: ParallelObjectIterator* _poi; KlassInfoTable* _shared_cit; BoolObjectClosure* _filter; - size_t _shared_missed_count; + uint _missed_count; bool _success; Mutex _mutex; @@ -244,12 +243,12 @@ _poi(poi), _shared_cit(shared_cit), _filter(filter), - _shared_missed_count(0), + _missed_count(0), _success(true), _mutex(Mutex::leaf, "Parallel heap iteration data merge lock") {} uint missed_count() const { - return _shared_missed_count; + return _missed_count; } bool success() { --- old/src/hotspot/share/services/attachListener.cpp 2020-07-09 14:50:45.768346768 +0800 +++ new/src/hotspot/share/services/attachListener.cpp 2020-07-09 14:50:45.616347250 +0800 @@ -242,100 +242,52 @@ return JNI_OK; } -// Valid Arguments: -// "-live" or "-all" -// "parallel=" -// "" -static jint process_heap_inspect_options(const char* argline, - outputStream* out, - HeapInspectArgs* args) { - char* save_ptr; - char* buf = NEW_C_HEAP_ARRAY(char, strlen(argline)+1, mtInternal); - snprintf(buf, strlen(argline)+1, "%s", argline); - if (buf == NULL) { - return JNI_ERR; - } - char* arg = strtok_r(buf, ",", &save_ptr); - while (arg != NULL) { - // "-live" or "-all" - if (strcmp(arg, "-live") == 0) { - args->_live_object_only = true; - } else if (strcmp(arg, "-all") == 0) { - args->_live_object_only = false; - } else if (strncmp(arg, "parallel=", 9) == 0) { - char* num_str = &arg[9]; - uintx num = 0; - if (!Arguments::parse_uintx(num_str, &num, 0)) { - out->print_cr("Invalid parallel thread number"); - return JNI_ERR; - } - args->_parallel_thread_num = num; - } else { - // must be file path - assert(args->_path == NULL, "Must be"); - char* path = args->_path = NEW_C_HEAP_ARRAY(char, strlen(arg)+1, mtInternal); - if (path == NULL) { - out->print_cr("Out of internal memory."); - return JNI_ERR; - } - snprintf(path, strlen(arg)+1, "%s", arg); - if (path[0] == '\0') { - out->print_cr("No dump file specified."); - } else { - fileStream* fs = new (ResourceObj::C_HEAP, mtInternal) fileStream(path); - if (fs == NULL) { - out->print_cr("Failed to allocate filestream for file: %s", path); - return JNI_ERR; - } - args->_fs = fs; - } - } - arg = strtok_r(NULL, ",", &save_ptr); - } - FREE_C_HEAP_ARRAY(char, buf); - return JNI_OK; -} - -// Parse command options -static jint parse_cmd_options(const char* cmd, const char* argline, - outputStream* out, void* args) { - assert(argline != NULL, "Must be"); - if (strncmp(cmd, "heap_inspection", 11) == 0) { - HeapInspectArgs* insp_opts = (HeapInspectArgs*)args; - return process_heap_inspect_options(argline, out, insp_opts); - } - // Command not match - return JNI_ERR; -} - // Implementation of "inspectheap" command // See also: ClassHistogramDCmd class // // Input arguments :- -// all arguments in op->arg(0); +// arg0: "-live" or "-all" +// arg1: Name of the dump file or NULL +// arg2: parallel thread number static jint heap_inspection(AttachOperation* op, outputStream* out) { bool live_objects_only = true; // default is true to retain the behavior before this change is made outputStream* os = out; // if path not specified or path is NULL, use out + fileStream* fs = NULL; const char* arg0 = op->arg(0); - size_t parallel_thread_num = os::processor_count() * 3 / 8; // default is less than half of processors. - HeapInspectArgs args; - // Parse arguments - if (arg0 != NULL) { - if (JNI_ERR == parse_cmd_options("heap_inspection", arg0, out, (void*)(&args))) { + uint parallel_thread_num = MAX(1, os::processor_count() * 3 / 8); // default is less than half of processors. + if (arg0 != NULL && (strlen(arg0) > 0)) { + if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) { + out->print_cr("Invalid argument to inspectheap operation: %s", arg0); return JNI_ERR; } - live_objects_only = args._live_object_only; - os = args._fs == NULL ? out : args._fs; - parallel_thread_num = args._parallel_thread_num == 0 ? parallel_thread_num : args._parallel_thread_num; - if (parallel_thread_num == 0) { - parallel_thread_num = 1; + live_objects_only = strcmp(arg0, "-live") == 0; + } + + const char* path = op->arg(1); + if (path != NULL && path[0] != '\0') { + // create file + fs = new (ResourceObj::C_HEAP, mtInternal) fileStream(path); + if (fs == NULL) { + out->print_cr("Failed to allocate space for file: %s", path); + } + os = fs; + } + + const char* num_str = op->arg(2); + if (num_str != NULL && num_str[0] != '\0') { + uintx num; + if (!Arguments::parse_uintx(num_str, &num, 0)) { + out->print_cr("Invalid parallel thread number: [%s]", num_str); + return JNI_ERR; } + parallel_thread_num = num == 0 ? parallel_thread_num : num; } VM_GC_HeapInspection heapop(os, live_objects_only /* request full gc */, parallel_thread_num); VMThread::execute(&heapop); - if (args._path != NULL) { - out->print_cr("Heap inspection file created: %s", args._path); + if (os != NULL && os != out) { + out->print_cr("Heap inspection file created: %s", path); + delete fs; } return JNI_OK; } --- old/src/hotspot/share/services/attachListener.hpp 2020-07-09 14:50:46.308345052 +0800 +++ new/src/hotspot/share/services/attachListener.hpp 2020-07-09 14:50:46.148345561 +0800 @@ -190,35 +190,6 @@ // complete operation by sending result code and any result data to the client virtual void complete(jint result, bufferedStream* result_stream) = 0; }; - -// Base Class for arguments parsing. -class CommandArgs : public CHeapObj { -}; - -// Arguments of HeapInspect. -struct HeapInspectArgs : public CommandArgs { - bool _live_object_only; - size_t _parallel_thread_num; - fileStream* _fs; - char* _path; - - HeapInspectArgs() : _live_object_only(false), - _parallel_thread_num(0), - _fs(NULL), - _path(NULL) { } - ~HeapInspectArgs() { - if (_path != NULL) { - FREE_C_HEAP_ARRAY(char, _path); - _path = NULL; - } - - if (_fs != NULL) { - delete _fs; - _fs = NULL; - } - } -}; - #endif // INCLUDE_SERVICES #endif // SHARE_SERVICES_ATTACHLISTENER_HPP --- old/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java 2020-07-09 14:50:46.904343159 +0800 +++ new/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java 2020-07-09 14:50:46.696343819 +0800 @@ -164,13 +164,6 @@ return null; } - private static String add_option(String cmd, String opt) { - if (cmd.isEmpty()) { - return opt; - } - return cmd + "," + opt; - } - private static void histo(String pid, String options) throws AttachNotSupportedException, IOException, UnsupportedEncodingException { @@ -180,30 +173,26 @@ String subopts[] = options.split(","); boolean set_all = false; boolean set_live = false; - String cmdline = ""; for (int i = 0; i < subopts.length; i++) { String subopt = subopts[i]; if (subopt.equals("") || subopt.equals("all")) { - cmdline = add_option(cmdline, "-all"); set_all = true; + liveopt = "-all"; } else if (subopt.equals("live")) { // Add '-' for compatibility. - cmdline = add_option(cmdline, "-live"); set_live = true; + liveopt = "-live"; } else if (subopt.startsWith("file=")) { filename = parseFileName(subopt); if (filename == null) { usage(1); // invalid options or no filename } - cmdline = add_option(cmdline, filename); } else if (subopt.startsWith("parallel=")) { parallel = subopt.substring("parallel=".length()); if (parallel == null) { usage(1); } - // Add "parallelThreadsNum=<>" for later check - cmdline = add_option(cmdline, subopt); } else { usage(1); } @@ -214,7 +203,7 @@ System.out.flush(); // inspectHeap is not the same as jcmd GC.class_histogram - executeCommandForPid(pid, "inspectheap", cmdline); + executeCommandForPid(pid, "inspectheap", liveopt, filename, parallel); } private static void dump(String pid, String options)