mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-31 13:15:26 +00:00
8201572: Improve Metaspace Statistics
Reviewed-by: adinn, zgu
This commit is contained in:
parent
4fc0229dcb
commit
d9b3c3203e
@ -110,6 +110,9 @@ enum ChunkIndex {
|
||||
size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunk_type, bool is_class);
|
||||
ChunkIndex get_chunk_type_by_size(size_t size, bool is_class);
|
||||
|
||||
ChunkIndex next_chunk_index(ChunkIndex i);
|
||||
ChunkIndex prev_chunk_index(ChunkIndex i);
|
||||
|
||||
// Returns a descriptive name for a chunk type.
|
||||
const char* chunk_size_name(ChunkIndex index);
|
||||
|
||||
@ -184,7 +187,7 @@ class Metachunk : public Metabase<Metachunk> {
|
||||
// Alignment of each allocation in the chunks.
|
||||
static size_t object_alignment();
|
||||
|
||||
// Size of the Metachunk header, including alignment.
|
||||
// Size of the Metachunk header, in words, including alignment.
|
||||
static size_t overhead();
|
||||
|
||||
Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -68,6 +68,11 @@ class SpaceManager;
|
||||
class VirtualSpaceList;
|
||||
class CollectedHeap;
|
||||
|
||||
namespace metaspace {
|
||||
namespace internals {
|
||||
class ClassLoaderMetaspaceStatistics;
|
||||
}}
|
||||
|
||||
// Metaspaces each have a SpaceManager and allocations
|
||||
// are done by the SpaceManager. Allocations are done
|
||||
// out of the current Metachunk. When the current Metachunk
|
||||
@ -94,10 +99,12 @@ class Metaspace : public AllStatic {
|
||||
MetadataTypeCount
|
||||
};
|
||||
enum MetaspaceType {
|
||||
StandardMetaspaceType,
|
||||
BootMetaspaceType,
|
||||
AnonymousMetaspaceType,
|
||||
ReflectionMetaspaceType
|
||||
ZeroMetaspaceType = 0,
|
||||
StandardMetaspaceType = ZeroMetaspaceType,
|
||||
BootMetaspaceType = StandardMetaspaceType + 1,
|
||||
AnonymousMetaspaceType = BootMetaspaceType + 1,
|
||||
ReflectionMetaspaceType = AnonymousMetaspaceType + 1,
|
||||
MetaspaceTypeCount
|
||||
};
|
||||
|
||||
private:
|
||||
@ -193,7 +200,6 @@ class Metaspace : public AllStatic {
|
||||
|
||||
static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size,
|
||||
MetaspaceObj::Type type, TRAPS);
|
||||
void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
|
||||
|
||||
static bool contains(const void* ptr);
|
||||
static bool contains_non_shared(const void* ptr);
|
||||
@ -238,96 +244,97 @@ class ClassLoaderMetaspace : public CHeapObj<mtClass> {
|
||||
void initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype);
|
||||
Metachunk* get_initialization_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype);
|
||||
|
||||
const Metaspace::MetaspaceType _space_type;
|
||||
Mutex* const _lock;
|
||||
SpaceManager* _vsm;
|
||||
SpaceManager* vsm() const { return _vsm; }
|
||||
|
||||
SpaceManager* _class_vsm;
|
||||
|
||||
SpaceManager* vsm() const { return _vsm; }
|
||||
SpaceManager* class_vsm() const { return _class_vsm; }
|
||||
SpaceManager* get_space_manager(Metaspace::MetadataType mdtype) {
|
||||
assert(mdtype != Metaspace::MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
|
||||
return mdtype == Metaspace::ClassType ? class_vsm() : vsm();
|
||||
}
|
||||
|
||||
Mutex* lock() const { return _lock; }
|
||||
|
||||
MetaWord* expand_and_allocate(size_t size, Metaspace::MetadataType mdtype);
|
||||
|
||||
size_t class_chunk_size(size_t word_size);
|
||||
|
||||
// Adds to the given statistic object. Must be locked with CLD metaspace lock.
|
||||
void add_to_statistics_locked(metaspace::internals::ClassLoaderMetaspaceStatistics* out) const;
|
||||
|
||||
public:
|
||||
|
||||
ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type);
|
||||
~ClassLoaderMetaspace();
|
||||
|
||||
Metaspace::MetaspaceType space_type() const { return _space_type; }
|
||||
|
||||
// Allocate space for metadata of type mdtype. This is space
|
||||
// within a Metachunk and is used by
|
||||
// allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS)
|
||||
MetaWord* allocate(size_t word_size, Metaspace::MetadataType mdtype);
|
||||
|
||||
size_t used_words_slow(Metaspace::MetadataType mdtype) const;
|
||||
size_t free_words_slow(Metaspace::MetadataType mdtype) const;
|
||||
size_t capacity_words_slow(Metaspace::MetadataType mdtype) const;
|
||||
|
||||
size_t used_bytes_slow(Metaspace::MetadataType mdtype) const;
|
||||
size_t capacity_bytes_slow(Metaspace::MetadataType mdtype) const;
|
||||
|
||||
size_t allocated_blocks_bytes() const;
|
||||
size_t allocated_chunks_bytes() const;
|
||||
|
||||
void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
|
||||
|
||||
void dump(outputStream* const out) const;
|
||||
|
||||
void print_on(outputStream* st) const;
|
||||
// Debugging support
|
||||
void verify();
|
||||
|
||||
// Adds to the given statistic object. Will lock with CLD metaspace lock.
|
||||
void add_to_statistics(metaspace::internals::ClassLoaderMetaspaceStatistics* out) const;
|
||||
|
||||
}; // ClassLoaderMetaspace
|
||||
|
||||
|
||||
class MetaspaceUtils : AllStatic {
|
||||
|
||||
// Spacemanager updates running counters.
|
||||
friend class SpaceManager;
|
||||
|
||||
// Running counters for statistics concerning in-use chunks.
|
||||
// Note: capacity = used + free + waste + overhead. Note that we do not
|
||||
// count free and waste. Their sum can be deduces from the three other values.
|
||||
// For more details, one should call print_report() from within a safe point.
|
||||
static size_t _capacity_words [Metaspace:: MetadataTypeCount];
|
||||
static size_t _overhead_words [Metaspace:: MetadataTypeCount];
|
||||
static volatile size_t _used_words [Metaspace:: MetadataTypeCount];
|
||||
|
||||
// Atomically decrement or increment in-use statistic counters
|
||||
static void dec_capacity(Metaspace::MetadataType mdtype, size_t words);
|
||||
static void inc_capacity(Metaspace::MetadataType mdtype, size_t words);
|
||||
static void dec_used(Metaspace::MetadataType mdtype, size_t words);
|
||||
static void inc_used(Metaspace::MetadataType mdtype, size_t words);
|
||||
static void dec_overhead(Metaspace::MetadataType mdtype, size_t words);
|
||||
static void inc_overhead(Metaspace::MetadataType mdtype, size_t words);
|
||||
|
||||
|
||||
// Getters for the in-use counters.
|
||||
static size_t capacity_words(Metaspace::MetadataType mdtype) { return _capacity_words[mdtype]; }
|
||||
static size_t used_words(Metaspace::MetadataType mdtype) { return _used_words[mdtype]; }
|
||||
static size_t overhead_words(Metaspace::MetadataType mdtype) { return _overhead_words[mdtype]; }
|
||||
|
||||
static size_t free_chunks_total_words(Metaspace::MetadataType mdtype);
|
||||
|
||||
// These methods iterate over the classloader data graph
|
||||
// for the given Metaspace type. These are slow.
|
||||
static size_t used_bytes_slow(Metaspace::MetadataType mdtype);
|
||||
static size_t free_bytes_slow(Metaspace::MetadataType mdtype);
|
||||
static size_t capacity_bytes_slow(Metaspace::MetadataType mdtype);
|
||||
static size_t capacity_bytes_slow();
|
||||
// Helper for print_xx_report.
|
||||
static void print_vs(outputStream* out, size_t scale);
|
||||
|
||||
// Running sum of space in all Metachunks that has been
|
||||
// allocated to a Metaspace. This is used instead of
|
||||
// iterating over all the classloaders. One for each
|
||||
// type of Metadata
|
||||
static size_t _capacity_words[Metaspace:: MetadataTypeCount];
|
||||
// Running sum of space in all Metachunks that
|
||||
// are being used for metadata. One for each
|
||||
// type of Metadata.
|
||||
static volatile size_t _used_words[Metaspace:: MetadataTypeCount];
|
||||
public:
|
||||
|
||||
public:
|
||||
// Decrement and increment _allocated_capacity_words
|
||||
static void dec_capacity(Metaspace::MetadataType type, size_t words);
|
||||
static void inc_capacity(Metaspace::MetadataType type, size_t words);
|
||||
|
||||
// Decrement and increment _allocated_used_words
|
||||
static void dec_used(Metaspace::MetadataType type, size_t words);
|
||||
static void inc_used(Metaspace::MetadataType type, size_t words);
|
||||
|
||||
// Total of space allocated to metadata in all Metaspaces.
|
||||
// This sums the space used in each Metachunk by
|
||||
// iterating over the classloader data graph
|
||||
static size_t used_bytes_slow() {
|
||||
return used_bytes_slow(Metaspace::ClassType) +
|
||||
used_bytes_slow(Metaspace::NonClassType);
|
||||
}
|
||||
// Collect used metaspace statistics. This involves walking the CLDG. The resulting
|
||||
// output will be the accumulated values for all live metaspaces.
|
||||
// Note: method does not do any locking.
|
||||
static void collect_statistics(metaspace::internals::ClassLoaderMetaspaceStatistics* out);
|
||||
|
||||
// Used by MetaspaceCounters
|
||||
static size_t free_chunks_total_words();
|
||||
static size_t free_chunks_total_bytes();
|
||||
static size_t free_chunks_total_bytes(Metaspace::MetadataType mdtype);
|
||||
|
||||
static size_t capacity_words(Metaspace::MetadataType mdtype) {
|
||||
return _capacity_words[mdtype];
|
||||
}
|
||||
static size_t capacity_words() {
|
||||
return capacity_words(Metaspace::NonClassType) +
|
||||
capacity_words(Metaspace::ClassType);
|
||||
@ -339,9 +346,6 @@ class MetaspaceUtils : AllStatic {
|
||||
return capacity_words() * BytesPerWord;
|
||||
}
|
||||
|
||||
static size_t used_words(Metaspace::MetadataType mdtype) {
|
||||
return _used_words[mdtype];
|
||||
}
|
||||
static size_t used_words() {
|
||||
return used_words(Metaspace::NonClassType) +
|
||||
used_words(Metaspace::ClassType);
|
||||
@ -353,8 +357,9 @@ class MetaspaceUtils : AllStatic {
|
||||
return used_words() * BytesPerWord;
|
||||
}
|
||||
|
||||
static size_t free_bytes();
|
||||
static size_t free_bytes(Metaspace::MetadataType mdtype);
|
||||
// Space committed but yet unclaimed by any class loader.
|
||||
static size_t free_in_vs_bytes();
|
||||
static size_t free_in_vs_bytes(Metaspace::MetadataType mdtype);
|
||||
|
||||
static size_t reserved_bytes(Metaspace::MetadataType mdtype);
|
||||
static size_t reserved_bytes() {
|
||||
@ -373,7 +378,29 @@ class MetaspaceUtils : AllStatic {
|
||||
return min_chunk_size_words() * BytesPerWord;
|
||||
}
|
||||
|
||||
static void print_metadata_for_nmt(outputStream* out, size_t scale = K);
|
||||
// Flags for print_report().
|
||||
enum ReportFlag {
|
||||
// Show usage by class loader.
|
||||
rf_show_loaders = (1 << 0),
|
||||
// Breaks report down by chunk type (small, medium, ...).
|
||||
rf_break_down_by_chunktype = (1 << 1),
|
||||
// Breaks report down by space type (anonymous, reflection, ...).
|
||||
rf_break_down_by_spacetype = (1 << 2),
|
||||
// Print details about the underlying virtual spaces.
|
||||
rf_show_vslist = (1 << 3),
|
||||
// Print metaspace map.
|
||||
rf_show_vsmap = (1 << 4)
|
||||
};
|
||||
|
||||
// This will print out a basic metaspace usage report but
|
||||
// unlike print_report() is guaranteed not to lock or to walk the CLDG.
|
||||
static void print_basic_report(outputStream* st, size_t scale);
|
||||
|
||||
// Prints a report about the current metaspace state.
|
||||
// Optional parts can be enabled via flags.
|
||||
// Function will walk the CLDG and will lock the expand lock; if that is not
|
||||
// convenient, use print_basic_report() instead.
|
||||
static void print_report(outputStream* out, size_t scale = 0, int flags = 0);
|
||||
|
||||
static bool has_chunk_free_list(Metaspace::MetadataType mdtype);
|
||||
static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype);
|
||||
@ -381,20 +408,13 @@ class MetaspaceUtils : AllStatic {
|
||||
// Print change in used metadata.
|
||||
static void print_metaspace_change(size_t prev_metadata_used);
|
||||
static void print_on(outputStream * out);
|
||||
static void print_on(outputStream * out, Metaspace::MetadataType mdtype);
|
||||
|
||||
static void print_class_waste(outputStream* out);
|
||||
static void print_waste(outputStream* out);
|
||||
|
||||
// Prints an ASCII representation of the given space.
|
||||
static void print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype);
|
||||
|
||||
static void dump(outputStream* out);
|
||||
static void verify_free_chunks();
|
||||
// Checks that the values returned by allocated_capacity_bytes() and
|
||||
// capacity_bytes_slow() are the same.
|
||||
static void verify_capacity();
|
||||
static void verify_used();
|
||||
// Check internal counters (capacity, used).
|
||||
static void verify_metrics();
|
||||
};
|
||||
|
||||
|
||||
132
src/hotspot/share/memory/metaspace/metaspaceCommon.cpp
Normal file
132
src/hotspot/share/memory/metaspace/metaspaceCommon.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, SAP and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
|
||||
#include "memory/metaspace/metaspaceCommon.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
namespace metaspace {
|
||||
namespace internals {
|
||||
|
||||
// Print a size, in words, scaled.
|
||||
void print_scaled_words(outputStream* st, size_t word_size, size_t scale, int width) {
|
||||
print_human_readable_size(st, word_size * sizeof(MetaWord), scale, width);
|
||||
}
|
||||
|
||||
// Convenience helper: prints a size value and a percentage.
|
||||
void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale, int width) {
|
||||
print_scaled_words(st, word_size, scale, width);
|
||||
st->print(" (");
|
||||
print_percentage(st, compare_word_size, word_size);
|
||||
st->print(")");
|
||||
}
|
||||
|
||||
|
||||
// Print a human readable size.
|
||||
// byte_size: size, in bytes, to be printed.
|
||||
// scale: one of 1 (byte-wise printing), sizeof(word) (word-size printing), K, M, G (scaled by KB, MB, GB respectively,
|
||||
// or 0, which means the best scale is choosen dynamically.
|
||||
// width: printing width.
|
||||
void print_human_readable_size(outputStream* st, size_t byte_size, size_t scale, int width) {
|
||||
if (scale == 0) {
|
||||
// Dynamic mode. Choose scale for this value.
|
||||
if (byte_size == 0) {
|
||||
// Zero values are printed as bytes.
|
||||
scale = 1;
|
||||
} else {
|
||||
if (byte_size >= G) {
|
||||
scale = G;
|
||||
} else if (byte_size >= M) {
|
||||
scale = M;
|
||||
} else if (byte_size >= K) {
|
||||
scale = K;
|
||||
} else {
|
||||
scale = 1;
|
||||
}
|
||||
}
|
||||
return print_human_readable_size(st, byte_size, scale, width);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
assert(scale == 1 || scale == BytesPerWord || scale == K || scale == M || scale == G, "Invalid scale");
|
||||
// Special case: printing wordsize should only be done with word-sized values
|
||||
if (scale == BytesPerWord) {
|
||||
assert(byte_size % BytesPerWord == 0, "not word sized");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (scale == 1) {
|
||||
st->print("%*" PRIuPTR " bytes", width, byte_size);
|
||||
} else if (scale == BytesPerWord) {
|
||||
st->print("%*" PRIuPTR " words", width, byte_size / BytesPerWord);
|
||||
} else {
|
||||
const char* display_unit = "";
|
||||
switch(scale) {
|
||||
case 1: display_unit = "bytes"; break;
|
||||
case BytesPerWord: display_unit = "words"; break;
|
||||
case K: display_unit = "KB"; break;
|
||||
case M: display_unit = "MB"; break;
|
||||
case G: display_unit = "GB"; break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
float display_value = (float) byte_size / scale;
|
||||
// Since we use width to display a number with two trailing digits, increase it a bit.
|
||||
width += 3;
|
||||
// Prevent very small but non-null values showing up as 0.00.
|
||||
if (byte_size > 0 && display_value < 0.01f) {
|
||||
st->print("%*s %s", width, "<0.01", display_unit);
|
||||
} else {
|
||||
st->print("%*.2f %s", width, display_value, display_unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prints a percentage value. Values smaller than 1% but not 0 are displayed as "<1%", values
|
||||
// larger than 99% but not 100% are displayed as ">100%".
|
||||
void print_percentage(outputStream* st, size_t total, size_t part) {
|
||||
if (total == 0) {
|
||||
st->print(" ?%%");
|
||||
} else if (part == 0) {
|
||||
st->print(" 0%%");
|
||||
} else if (part == total) {
|
||||
st->print("100%%");
|
||||
} else {
|
||||
// Note: clearly print very-small-but-not-0% and very-large-but-not-100% percentages.
|
||||
float p = ((float)part / total) * 100.0f;
|
||||
if (p < 1.0f) {
|
||||
st->print(" <1%%");
|
||||
} else if (p > 99.0f){
|
||||
st->print(">99%%");
|
||||
} else {
|
||||
st->print("%3.0f%%", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internals
|
||||
} // namespace metaspace
|
||||
56
src/hotspot/share/memory/metaspace/metaspaceCommon.hpp
Normal file
56
src/hotspot/share/memory/metaspace/metaspaceCommon.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP_
|
||||
#define SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP_
|
||||
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
|
||||
class outputStream;
|
||||
|
||||
namespace metaspace {
|
||||
namespace internals {
|
||||
|
||||
// Print a size, in words, scaled.
|
||||
void print_scaled_words(outputStream* st, size_t word_size, size_t scale = 0, int width = -1);
|
||||
|
||||
// Convenience helper: prints a size value and a percentage.
|
||||
void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale = 0, int width = -1);
|
||||
|
||||
// Print a human readable size.
|
||||
// byte_size: size, in bytes, to be printed.
|
||||
// scale: one of 1 (byte-wise printing), sizeof(word) (word-size printing), K, M, G (scaled by KB, MB, GB respectively,
|
||||
// or 0, which means the best scale is choosen dynamically.
|
||||
// width: printing width.
|
||||
void print_human_readable_size(outputStream* st, size_t byte_size, size_t scale = 0, int width = -1);
|
||||
|
||||
// Prints a percentage value. Values smaller than 1% but not 0 are displayed as "<1%", values
|
||||
// larger than 99% but not 100% are displayed as ">100%".
|
||||
void print_percentage(outputStream* st, size_t total, size_t part);
|
||||
|
||||
} // namespace internals
|
||||
} // namespace metaspace
|
||||
|
||||
#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */
|
||||
100
src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp
Normal file
100
src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
#include "precompiled.hpp"
|
||||
#include "memory/metaspace.hpp"
|
||||
#include "memory/metaspace/metaspaceDCmd.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "services/diagnosticCommand.hpp"
|
||||
#include "services/nmtCommon.hpp"
|
||||
|
||||
namespace metaspace {
|
||||
|
||||
MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap)
|
||||
: DCmdWithParser(output, heap)
|
||||
, _basic("basic", "Prints a basic summary (does not need a safepoint).", "BOOLEAN", false, "false")
|
||||
, _show_loaders("show-loaders", "Shows usage by class loader.", "BOOLEAN", false, "false")
|
||||
, _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false")
|
||||
, _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false")
|
||||
, _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false")
|
||||
, _show_vsmap("vsmap", "Shows chunk composition of the underlying virtual spaces", "BOOLEAN", false, "false")
|
||||
, _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) "
|
||||
"or \"dynamic\" for a dynamically choosen scale.",
|
||||
"STRING", false, "dynamic")
|
||||
{
|
||||
_dcmdparser.add_dcmd_option(&_basic);
|
||||
_dcmdparser.add_dcmd_option(&_show_loaders);
|
||||
_dcmdparser.add_dcmd_option(&_by_chunktype);
|
||||
_dcmdparser.add_dcmd_option(&_by_spacetype);
|
||||
_dcmdparser.add_dcmd_option(&_show_vslist);
|
||||
_dcmdparser.add_dcmd_option(&_show_vsmap);
|
||||
_dcmdparser.add_dcmd_option(&_scale);
|
||||
}
|
||||
|
||||
int MetaspaceDCmd::num_arguments() {
|
||||
ResourceMark rm;
|
||||
MetaspaceDCmd* dcmd = new MetaspaceDCmd(NULL, false);
|
||||
if (dcmd != NULL) {
|
||||
DCmdMark mark(dcmd);
|
||||
return dcmd->_dcmdparser.num_arguments();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MetaspaceDCmd::execute(DCmdSource source, TRAPS) {
|
||||
// Parse scale value.
|
||||
const char* scale_value = _scale.value();
|
||||
size_t scale = 0;
|
||||
if (scale_value != NULL) {
|
||||
if (strcasecmp("dynamic", scale_value) == 0) {
|
||||
scale = 0;
|
||||
} else {
|
||||
scale = NMTUtil::scale_from_name(scale_value);
|
||||
if (scale == 0) {
|
||||
output()->print_cr("Invalid scale: \"%s\". Will use dynamic scaling.", scale_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_basic.value() == true) {
|
||||
if (_show_loaders.value() || _by_chunktype.value() || _by_spacetype.value() ||
|
||||
_show_vslist.value() || _show_vsmap.value()) {
|
||||
// Basic mode. Just print essentials. Does not need to be at a safepoint.
|
||||
output()->print_cr("In basic mode, additional arguments are ignored.");
|
||||
}
|
||||
MetaspaceUtils::print_basic_report(output(), scale);
|
||||
} else {
|
||||
// Full mode. Requires safepoint.
|
||||
int flags = 0;
|
||||
if (_show_loaders.value()) flags |= MetaspaceUtils::rf_show_loaders;
|
||||
if (_by_chunktype.value()) flags |= MetaspaceUtils::rf_break_down_by_chunktype;
|
||||
if (_by_spacetype.value()) flags |= MetaspaceUtils::rf_break_down_by_spacetype;
|
||||
if (_show_vslist.value()) flags |= MetaspaceUtils::rf_show_vslist;
|
||||
if (_show_vsmap.value()) flags |= MetaspaceUtils::rf_show_vsmap;
|
||||
VM_PrintMetadata op(output(), scale, flags);
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace metaspace
|
||||
|
||||
@ -21,16 +21,44 @@
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
#include "precompiled.hpp"
|
||||
#include "memory/metaspace.hpp"
|
||||
|
||||
#ifndef SHARE_MEMORY_METASPACE_METASPACEDCMD_HPP_
|
||||
#define SHARE_MEMORY_METASPACE_METASPACEDCMD_HPP_
|
||||
|
||||
#include "services/diagnosticCommand.hpp"
|
||||
|
||||
MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap): DCmd(output, heap) {
|
||||
}
|
||||
class outputStream;
|
||||
|
||||
void MetaspaceDCmd::execute(DCmdSource source, TRAPS) {
|
||||
const size_t scale = 1 * K;
|
||||
VM_PrintMetadata op(output(), scale);
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
namespace metaspace {
|
||||
|
||||
class MetaspaceDCmd : public DCmdWithParser {
|
||||
DCmdArgument<bool> _basic;
|
||||
DCmdArgument<bool> _show_loaders;
|
||||
DCmdArgument<bool> _by_spacetype;
|
||||
DCmdArgument<bool> _by_chunktype;
|
||||
DCmdArgument<bool> _show_vslist;
|
||||
DCmdArgument<bool> _show_vsmap;
|
||||
DCmdArgument<char*> _scale;
|
||||
public:
|
||||
MetaspaceDCmd(outputStream* output, bool heap);
|
||||
static const char* name() {
|
||||
return "VM.metaspace";
|
||||
}
|
||||
static const char* description() {
|
||||
return "Prints the statistics for the metaspace";
|
||||
}
|
||||
static const char* impact() {
|
||||
return "Medium: Depends on number of classes loaded.";
|
||||
}
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"monitor", NULL};
|
||||
return p;
|
||||
}
|
||||
static int num_arguments();
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
} // namespace metaspace
|
||||
|
||||
#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */
|
||||
278
src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp
Normal file
278
src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, SAP and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
#include "precompiled.hpp"
|
||||
|
||||
#include "memory/metachunk.hpp"
|
||||
#include "memory/metaspace/metaspaceCommon.hpp"
|
||||
#include "memory/metaspace/metaspaceStatistics.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
namespace metaspace {
|
||||
namespace internals {
|
||||
|
||||
// FreeChunksStatistics methods
|
||||
|
||||
FreeChunksStatistics::FreeChunksStatistics()
|
||||
: _num(0), _cap(0)
|
||||
{}
|
||||
|
||||
void FreeChunksStatistics::reset() {
|
||||
_num = 0; _cap = 0;
|
||||
}
|
||||
|
||||
void FreeChunksStatistics::add(uintx n, size_t s) {
|
||||
_num += n; _cap += s;
|
||||
}
|
||||
|
||||
void FreeChunksStatistics::add(const FreeChunksStatistics& other) {
|
||||
_num += other._num;
|
||||
_cap += other._cap;
|
||||
}
|
||||
|
||||
void FreeChunksStatistics::print_on(outputStream* st, size_t scale) const {
|
||||
st->print(UINTX_FORMAT, _num);
|
||||
st->print(" chunks, total capacity ");
|
||||
print_scaled_words(st, _cap, scale);
|
||||
}
|
||||
|
||||
// ChunkManagerStatistics methods
|
||||
|
||||
void ChunkManagerStatistics::reset() {
|
||||
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
|
||||
_chunk_stats[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
size_t ChunkManagerStatistics::total_capacity() const {
|
||||
return _chunk_stats[SpecializedIndex].cap() +
|
||||
_chunk_stats[SmallIndex].cap() +
|
||||
_chunk_stats[MediumIndex].cap() +
|
||||
_chunk_stats[HumongousIndex].cap();
|
||||
}
|
||||
|
||||
void ChunkManagerStatistics::print_on(outputStream* st, size_t scale) const {
|
||||
FreeChunksStatistics totals;
|
||||
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
|
||||
st->cr();
|
||||
st->print("%12s chunks: ", chunk_size_name(i));
|
||||
if (_chunk_stats[i].num() > 0) {
|
||||
st->print(UINTX_FORMAT_W(4) ", capacity ", _chunk_stats[i].num());
|
||||
print_scaled_words(st, _chunk_stats[i].cap(), scale);
|
||||
} else {
|
||||
st->print("(none)");
|
||||
}
|
||||
totals.add(_chunk_stats[i]);
|
||||
}
|
||||
st->cr();
|
||||
st->print("%19s: " UINTX_FORMAT_W(4) ", capacity=", "Total", totals.num());
|
||||
print_scaled_words(st, totals.cap(), scale);
|
||||
st->cr();
|
||||
}
|
||||
|
||||
// UsedChunksStatistics methods
|
||||
|
||||
UsedChunksStatistics::UsedChunksStatistics()
|
||||
: _num(0), _cap(0), _used(0), _free(0), _waste(0), _overhead(0)
|
||||
{}
|
||||
|
||||
void UsedChunksStatistics::reset() {
|
||||
_num = 0;
|
||||
_cap = _overhead = _used = _free = _waste = 0;
|
||||
}
|
||||
|
||||
void UsedChunksStatistics::add(const UsedChunksStatistics& other) {
|
||||
_num += other._num;
|
||||
_cap += other._cap;
|
||||
_used += other._used;
|
||||
_free += other._free;
|
||||
_waste += other._waste;
|
||||
_overhead += other._overhead;
|
||||
DEBUG_ONLY(check_sanity());
|
||||
}
|
||||
|
||||
void UsedChunksStatistics::print_on(outputStream* st, size_t scale) const {
|
||||
int col = st->position();
|
||||
st->print(UINTX_FORMAT_W(4) " chunk%s, ", _num, _num != 1 ? "s" : "");
|
||||
if (_num > 0) {
|
||||
col += 14; st->fill_to(col);
|
||||
|
||||
print_scaled_words(st, _cap, scale, 5);
|
||||
st->print(" capacity, ");
|
||||
|
||||
col += 18; st->fill_to(col);
|
||||
print_scaled_words_and_percentage(st, _used, _cap, scale, 5);
|
||||
st->print(" used, ");
|
||||
|
||||
col += 20; st->fill_to(col);
|
||||
print_scaled_words_and_percentage(st, _free, _cap, scale, 5);
|
||||
st->print(" free, ");
|
||||
|
||||
col += 20; st->fill_to(col);
|
||||
print_scaled_words_and_percentage(st, _waste, _cap, scale, 5);
|
||||
st->print(" waste, ");
|
||||
|
||||
col += 20; st->fill_to(col);
|
||||
print_scaled_words_and_percentage(st, _overhead, _cap, scale, 5);
|
||||
st->print(" overhead");
|
||||
}
|
||||
DEBUG_ONLY(check_sanity());
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
void UsedChunksStatistics::check_sanity() const {
|
||||
assert(_overhead == (Metachunk::overhead() * _num), "Sanity: Overhead.");
|
||||
assert(_cap == _used + _free + _waste + _overhead, "Sanity: Capacity.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// SpaceManagerStatistics methods
|
||||
|
||||
SpaceManagerStatistics::SpaceManagerStatistics() { reset(); }
|
||||
|
||||
void SpaceManagerStatistics::reset() {
|
||||
for (int i = 0; i < NumberOfInUseLists; i ++) {
|
||||
_chunk_stats[i].reset();
|
||||
_free_blocks_num = 0; _free_blocks_cap_words = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SpaceManagerStatistics::add_free_blocks_info(uintx num, size_t cap) {
|
||||
_free_blocks_num += num;
|
||||
_free_blocks_cap_words += cap;
|
||||
}
|
||||
|
||||
void SpaceManagerStatistics::add(const SpaceManagerStatistics& other) {
|
||||
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
|
||||
_chunk_stats[i].add(other._chunk_stats[i]);
|
||||
}
|
||||
_free_blocks_num += other._free_blocks_num;
|
||||
_free_blocks_cap_words += other._free_blocks_cap_words;
|
||||
}
|
||||
|
||||
// Returns total chunk statistics over all chunk types.
|
||||
UsedChunksStatistics SpaceManagerStatistics::totals() const {
|
||||
UsedChunksStatistics stat;
|
||||
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
|
||||
stat.add(_chunk_stats[i]);
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
void SpaceManagerStatistics::print_on(outputStream* st, size_t scale, bool detailed) const {
|
||||
streamIndentor sti(st);
|
||||
if (detailed) {
|
||||
st->cr_indent();
|
||||
st->print("Usage by chunk type:");
|
||||
{
|
||||
streamIndentor sti2(st);
|
||||
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
|
||||
st->cr_indent();
|
||||
st->print("%15s: ", chunk_size_name(i));
|
||||
if (_chunk_stats[i].num() == 0) {
|
||||
st->print(" (none)");
|
||||
} else {
|
||||
_chunk_stats[i].print_on(st, scale);
|
||||
}
|
||||
}
|
||||
|
||||
st->cr_indent();
|
||||
st->print("%15s: ", "-total-");
|
||||
totals().print_on(st, scale);
|
||||
}
|
||||
if (_free_blocks_num > 0) {
|
||||
st->cr_indent();
|
||||
st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num);
|
||||
print_scaled_words(st, _free_blocks_cap_words, scale);
|
||||
}
|
||||
} else {
|
||||
totals().print_on(st, scale);
|
||||
st->print(", ");
|
||||
st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num);
|
||||
print_scaled_words(st, _free_blocks_cap_words, scale);
|
||||
}
|
||||
}
|
||||
|
||||
// ClassLoaderMetaspaceStatistics methods
|
||||
|
||||
ClassLoaderMetaspaceStatistics::ClassLoaderMetaspaceStatistics() { reset(); }
|
||||
|
||||
void ClassLoaderMetaspaceStatistics::reset() {
|
||||
nonclass_sm_stats().reset();
|
||||
if (Metaspace::using_class_space()) {
|
||||
class_sm_stats().reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns total space manager statistics for both class and non-class metaspace
|
||||
SpaceManagerStatistics ClassLoaderMetaspaceStatistics::totals() const {
|
||||
SpaceManagerStatistics stats;
|
||||
stats.add(nonclass_sm_stats());
|
||||
if (Metaspace::using_class_space()) {
|
||||
stats.add(class_sm_stats());
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
void ClassLoaderMetaspaceStatistics::add(const ClassLoaderMetaspaceStatistics& other) {
|
||||
nonclass_sm_stats().add(other.nonclass_sm_stats());
|
||||
if (Metaspace::using_class_space()) {
|
||||
class_sm_stats().add(other.class_sm_stats());
|
||||
}
|
||||
}
|
||||
|
||||
void ClassLoaderMetaspaceStatistics::print_on(outputStream* st, size_t scale, bool detailed) const {
|
||||
streamIndentor sti(st);
|
||||
st->cr_indent();
|
||||
if (Metaspace::using_class_space()) {
|
||||
st->print("Non-Class: ");
|
||||
}
|
||||
nonclass_sm_stats().print_on(st, scale, detailed);
|
||||
if (detailed) {
|
||||
st->cr();
|
||||
}
|
||||
if (Metaspace::using_class_space()) {
|
||||
st->cr_indent();
|
||||
st->print(" Class: ");
|
||||
class_sm_stats().print_on(st, scale, detailed);
|
||||
if (detailed) {
|
||||
st->cr();
|
||||
}
|
||||
st->cr_indent();
|
||||
st->print(" Both: ");
|
||||
totals().print_on(st, scale, detailed);
|
||||
if (detailed) {
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
st->cr();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace internals
|
||||
} // end namespace metaspace
|
||||
|
||||
|
||||
189
src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp
Normal file
189
src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, SAP and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_
|
||||
#define SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_
|
||||
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "memory/metachunk.hpp" // for ChunkIndex enum
|
||||
#include "memory/metaspace.hpp" // for MetadataType enum
|
||||
|
||||
class outputStream;
|
||||
|
||||
namespace metaspace {
|
||||
namespace internals {
|
||||
|
||||
// Contains statistics for a number of free chunks.
|
||||
class FreeChunksStatistics {
|
||||
uintx _num; // Number of chunks
|
||||
size_t _cap; // Total capacity, in words
|
||||
|
||||
public:
|
||||
FreeChunksStatistics();
|
||||
|
||||
void reset();
|
||||
|
||||
uintx num() const { return _num; }
|
||||
size_t cap() const { return _cap; }
|
||||
|
||||
void add(uintx n, size_t s);
|
||||
void add(const FreeChunksStatistics& other);
|
||||
void print_on(outputStream* st, size_t scale) const;
|
||||
|
||||
}; // end: FreeChunksStatistics
|
||||
|
||||
|
||||
// Contains statistics for a ChunkManager.
|
||||
class ChunkManagerStatistics {
|
||||
|
||||
FreeChunksStatistics _chunk_stats[NumberOfInUseLists];
|
||||
|
||||
public:
|
||||
|
||||
// Free chunk statistics, by chunk index.
|
||||
const FreeChunksStatistics& chunk_stats(ChunkIndex index) const { return _chunk_stats[index]; }
|
||||
FreeChunksStatistics& chunk_stats(ChunkIndex index) { return _chunk_stats[index]; }
|
||||
|
||||
void reset();
|
||||
size_t total_capacity() const;
|
||||
|
||||
void print_on(outputStream* st, size_t scale) const;
|
||||
|
||||
}; // ChunkManagerStatistics
|
||||
|
||||
// Contains statistics for a number of chunks in use.
|
||||
// Each chunk has a used and free portion; however, there are current chunks (serving
|
||||
// potential future metaspace allocations) and non-current chunks. Unused portion of the
|
||||
// former is counted as free, unused portion of the latter counts as waste.
|
||||
class UsedChunksStatistics {
|
||||
uintx _num; // Number of chunks
|
||||
size_t _cap; // Total capacity in words.
|
||||
size_t _used; // Total used area, in words
|
||||
size_t _free; // Total free area (unused portions of current chunks), in words
|
||||
size_t _waste; // Total waste area (unused portions of non-current chunks), in words
|
||||
size_t _overhead; // Total sum of chunk overheads, in words.
|
||||
|
||||
public:
|
||||
|
||||
UsedChunksStatistics();
|
||||
|
||||
void reset();
|
||||
|
||||
uintx num() const { return _num; }
|
||||
|
||||
// Total capacity, in words
|
||||
size_t cap() const { return _cap; }
|
||||
|
||||
// Total used area, in words
|
||||
size_t used() const { return _used; }
|
||||
|
||||
// Total free area (unused portions of current chunks), in words
|
||||
size_t free() const { return _free; }
|
||||
|
||||
// Total waste area (unused portions of non-current chunks), in words
|
||||
size_t waste() const { return _waste; }
|
||||
|
||||
// Total area spent in overhead (chunk headers), in words
|
||||
size_t overhead() const { return _overhead; }
|
||||
|
||||
void add_num(uintx n) { _num += n; }
|
||||
void add_cap(size_t s) { _cap += s; }
|
||||
void add_used(size_t s) { _used += s; }
|
||||
void add_free(size_t s) { _free += s; }
|
||||
void add_waste(size_t s) { _waste += s; }
|
||||
void add_overhead(size_t s) { _overhead += s; }
|
||||
|
||||
void add(const UsedChunksStatistics& other);
|
||||
|
||||
void print_on(outputStream* st, size_t scale) const;
|
||||
|
||||
#ifdef ASSERT
|
||||
void check_sanity() const;
|
||||
#endif
|
||||
|
||||
}; // UsedChunksStatistics
|
||||
|
||||
// Class containing statistics for one or more space managers.
|
||||
class SpaceManagerStatistics {
|
||||
|
||||
UsedChunksStatistics _chunk_stats[NumberOfInUseLists];
|
||||
uintx _free_blocks_num;
|
||||
size_t _free_blocks_cap_words;
|
||||
|
||||
public:
|
||||
|
||||
SpaceManagerStatistics();
|
||||
|
||||
// Chunk statistics by chunk index
|
||||
const UsedChunksStatistics& chunk_stats(ChunkIndex index) const { return _chunk_stats[index]; }
|
||||
UsedChunksStatistics& chunk_stats(ChunkIndex index) { return _chunk_stats[index]; }
|
||||
|
||||
uintx free_blocks_num () const { return _free_blocks_num; }
|
||||
size_t free_blocks_cap_words () const { return _free_blocks_cap_words; }
|
||||
|
||||
void reset();
|
||||
|
||||
void add_free_blocks_info(uintx num, size_t cap);
|
||||
|
||||
// Returns total chunk statistics over all chunk types.
|
||||
UsedChunksStatistics totals() const;
|
||||
|
||||
void add(const SpaceManagerStatistics& other);
|
||||
|
||||
void print_on(outputStream* st, size_t scale, bool detailed) const;
|
||||
|
||||
}; // SpaceManagerStatistics
|
||||
|
||||
class ClassLoaderMetaspaceStatistics {
|
||||
|
||||
SpaceManagerStatistics _sm_stats[Metaspace::MetadataTypeCount];
|
||||
|
||||
public:
|
||||
|
||||
ClassLoaderMetaspaceStatistics();
|
||||
|
||||
const SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) const { return _sm_stats[mdType]; }
|
||||
SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) { return _sm_stats[mdType]; }
|
||||
|
||||
const SpaceManagerStatistics& nonclass_sm_stats() const { return sm_stats(Metaspace::NonClassType); }
|
||||
SpaceManagerStatistics& nonclass_sm_stats() { return sm_stats(Metaspace::NonClassType); }
|
||||
const SpaceManagerStatistics& class_sm_stats() const { return sm_stats(Metaspace::ClassType); }
|
||||
SpaceManagerStatistics& class_sm_stats() { return sm_stats(Metaspace::ClassType); }
|
||||
|
||||
void reset();
|
||||
|
||||
void add(const ClassLoaderMetaspaceStatistics& other);
|
||||
|
||||
// Returns total space manager statistics for both class and non-class metaspace
|
||||
SpaceManagerStatistics totals() const;
|
||||
|
||||
void print_on(outputStream* st, size_t scale, bool detailed) const;
|
||||
|
||||
}; // ClassLoaderMetaspaceStatistics
|
||||
|
||||
} // namespace internals
|
||||
} // namespace metaspace
|
||||
|
||||
#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */
|
||||
@ -235,7 +235,7 @@ void VM_PrintJNI::doit() {
|
||||
}
|
||||
|
||||
void VM_PrintMetadata::doit() {
|
||||
MetaspaceUtils::print_metadata_for_nmt(_out, _scale);
|
||||
MetaspaceUtils::print_report(_out, _scale, _flags);
|
||||
}
|
||||
|
||||
VM_FindDeadlocks::~VM_FindDeadlocks() {
|
||||
|
||||
@ -391,10 +391,14 @@ class VM_PrintJNI: public VM_Operation {
|
||||
|
||||
class VM_PrintMetadata : public VM_Operation {
|
||||
private:
|
||||
outputStream* _out;
|
||||
size_t _scale;
|
||||
outputStream* const _out;
|
||||
const size_t _scale;
|
||||
const int _flags;
|
||||
|
||||
public:
|
||||
VM_PrintMetadata(outputStream* out, size_t scale) : _out(out), _scale(scale) {};
|
||||
VM_PrintMetadata(outputStream* out, size_t scale, int flags)
|
||||
: _out(out), _scale(scale), _flags(flags)
|
||||
{};
|
||||
|
||||
VMOp_Type type() const { return VMOp_PrintMetadata; }
|
||||
void doit();
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "compiler/directivesParser.hpp"
|
||||
#include "gc/shared/vmGCOperations.hpp"
|
||||
#include "memory/metaspace/metaspaceDCmd.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/objArrayOop.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
@ -90,7 +91,7 @@ void DCmdRegistrant::register_dcmds(){
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHierarchyDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SymboltableDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<StringtableDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<MetaspaceDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<metaspace::MetaspaceDCmd>(full_export, true, false));
|
||||
#if INCLUDE_JVMTI // Both JVMTI and SERVICES have to be enabled to have this dcmd
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JVMTIAgentLoadDCmd>(full_export, true, false));
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
@ -866,25 +866,4 @@ public:
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
class MetaspaceDCmd : public DCmd {
|
||||
public:
|
||||
MetaspaceDCmd(outputStream* output, bool heap);
|
||||
static const char* name() {
|
||||
return "VM.metaspace";
|
||||
}
|
||||
static const char* description() {
|
||||
return "Prints the statistics for the metaspace";
|
||||
}
|
||||
static const char* impact() {
|
||||
return "Medium: Depends on number of classes loaded.";
|
||||
}
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"monitor", NULL};
|
||||
return p;
|
||||
}
|
||||
static int num_arguments() { return 0; }
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
|
||||
|
||||
@ -201,7 +201,7 @@ void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const {
|
||||
size_t used = MetaspaceUtils::used_bytes(type);
|
||||
size_t free = (MetaspaceUtils::capacity_bytes(type) - used)
|
||||
+ MetaspaceUtils::free_chunks_total_bytes(type)
|
||||
+ MetaspaceUtils::free_bytes(type);
|
||||
+ MetaspaceUtils::free_in_vs_bytes(type);
|
||||
|
||||
assert(committed >= used + free, "Sanity");
|
||||
size_t waste = committed - (used + free);
|
||||
|
||||
@ -177,10 +177,12 @@ void MemTracker::report(bool summary_only, outputStream* output) {
|
||||
} else {
|
||||
MemDetailReporter rpt(baseline, output);
|
||||
rpt.report();
|
||||
|
||||
output->print("Metaspace:");
|
||||
// Metadata reporting requires a safepoint, so avoid it if VM is not in good state.
|
||||
assert(!VMError::fatal_error_in_progress(), "Do not report metadata in error report");
|
||||
VM_PrintMetadata vmop(output, K);
|
||||
VM_PrintMetadata vmop(output, K,
|
||||
MetaspaceUtils::rf_show_loaders |
|
||||
MetaspaceUtils::rf_break_down_by_spacetype);
|
||||
VMThread::execute(&vmop);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
*/
|
||||
#include "precompiled.hpp"
|
||||
#include "services/nmtCommon.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
const char* NMTUtil::_memory_type_names[] = {
|
||||
"Java Heap",
|
||||
@ -59,14 +60,13 @@ const char* NMTUtil::scale_name(size_t scale) {
|
||||
|
||||
size_t NMTUtil::scale_from_name(const char* scale) {
|
||||
assert(scale != NULL, "Null pointer check");
|
||||
if (strncmp(scale, "KB", 2) == 0 ||
|
||||
strncmp(scale, "kb", 2) == 0) {
|
||||
if (strcasecmp(scale, "1") == 0 || strcasecmp(scale, "b") == 0) {
|
||||
return 1;
|
||||
} else if (strcasecmp(scale, "kb") == 0 || strcasecmp(scale, "k") == 0) {
|
||||
return K;
|
||||
} else if (strncmp(scale, "MB", 2) == 0 ||
|
||||
strncmp(scale, "mb", 2) == 0) {
|
||||
} else if (strcasecmp(scale, "mb") == 0 || strcasecmp(scale, "m") == 0) {
|
||||
return M;
|
||||
} else if (strncmp(scale, "GB", 2) == 0 ||
|
||||
strncmp(scale, "gb", 2) == 0) {
|
||||
} else if (strcasecmp(scale, "gb") == 0 || strcasecmp(scale, "g") == 0) {
|
||||
return G;
|
||||
} else {
|
||||
return 0; // Invalid value
|
||||
|
||||
@ -617,7 +617,7 @@ void MetaspaceSnapshot::snapshot(Metaspace::MetadataType type, MetaspaceSnapshot
|
||||
|
||||
size_t free_in_bytes = (MetaspaceUtils::capacity_bytes(type) - MetaspaceUtils::used_bytes(type))
|
||||
+ MetaspaceUtils::free_chunks_total_bytes(type)
|
||||
+ MetaspaceUtils::free_bytes(type);
|
||||
+ MetaspaceUtils::free_in_vs_bytes(type);
|
||||
mss._free_in_bytes[type] = free_in_bytes;
|
||||
}
|
||||
|
||||
|
||||
@ -206,6 +206,10 @@ void outputStream::cr() {
|
||||
this->write("\n", 1);
|
||||
}
|
||||
|
||||
void outputStream::cr_indent() {
|
||||
cr(); indent();
|
||||
}
|
||||
|
||||
void outputStream::stamp() {
|
||||
if (! _stamp.is_updated()) {
|
||||
_stamp.update(); // start at 0 on first call to stamp()
|
||||
|
||||
@ -102,8 +102,10 @@ class outputStream : public ResourceObj {
|
||||
void put(char ch);
|
||||
void sp(int count = 1);
|
||||
void cr();
|
||||
void cr_indent();
|
||||
void bol() { if (_position > 0) cr(); }
|
||||
|
||||
|
||||
// Time stamp
|
||||
TimeStamp& time_stamp() { return _stamp; }
|
||||
void stamp();
|
||||
@ -152,7 +154,6 @@ class streamIndentor : public StackObj {
|
||||
~streamIndentor() { _str->dec(_amount); }
|
||||
};
|
||||
|
||||
|
||||
// advisory locking for the shared tty stream:
|
||||
class ttyLocker: StackObj {
|
||||
friend class ttyUnlocker;
|
||||
|
||||
@ -862,6 +862,13 @@ void VMError::report(outputStream* st, bool _verbose) {
|
||||
st->cr();
|
||||
}
|
||||
|
||||
STEP("printing metaspace information")
|
||||
|
||||
if (_verbose && Universe::is_fully_initialized()) {
|
||||
st->print_cr("Metaspace:");
|
||||
MetaspaceUtils::print_basic_report(st, 0);
|
||||
}
|
||||
|
||||
STEP("printing code cache information")
|
||||
|
||||
if (_verbose && Universe::is_fully_initialized()) {
|
||||
@ -1046,6 +1053,13 @@ void VMError::print_vm_info(outputStream* st) {
|
||||
st->cr();
|
||||
}
|
||||
|
||||
// STEP("printing metaspace information")
|
||||
|
||||
if (Universe::is_fully_initialized()) {
|
||||
st->print_cr("Metaspace:");
|
||||
MetaspaceUtils::print_basic_report(st, 0);
|
||||
}
|
||||
|
||||
// STEP("printing code cache information")
|
||||
|
||||
if (Universe::is_fully_initialized()) {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
# It also contains test-suite configuration information.
|
||||
|
||||
# The list of keywords supported in this test suite
|
||||
keys=cte_test jcmd nmt regression gc stress
|
||||
keys=cte_test jcmd nmt regression gc stress metaspace
|
||||
|
||||
groups=TEST.groups
|
||||
|
||||
|
||||
126
test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java
Normal file
126
test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, SAP and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.JDKToolFinder;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @key metaspace jcmd
|
||||
* @summary Test the VM.metaspace command
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run main/othervm -XX:MaxMetaspaceSize=201M -XX:+VerifyMetaspace -XX:+UseCompressedClassPointers PrintMetaspaceDcmd with-compressed-class-space
|
||||
* @run main/othervm -XX:MaxMetaspaceSize=201M -XX:+VerifyMetaspace -XX:-UseCompressedClassPointers PrintMetaspaceDcmd without-compressed-class-space
|
||||
*/
|
||||
|
||||
public class PrintMetaspaceDcmd {
|
||||
|
||||
// Run jcmd VM.metaspace against a VM with CompressedClassPointers on.
|
||||
// The report should detail Non-Class and Class portions separately.
|
||||
private static void doTheTest(boolean usesCompressedClassSpace) throws Exception {
|
||||
ProcessBuilder pb = new ProcessBuilder();
|
||||
OutputAnalyzer output;
|
||||
// Grab my own PID
|
||||
String pid = Long.toString(ProcessTools.getProcessId());
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "basic"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
if (usesCompressedClassSpace) {
|
||||
output.shouldContain("Non-Class:");
|
||||
output.shouldContain("Class:");
|
||||
}
|
||||
output.shouldContain("Virtual space:");
|
||||
output.shouldContain("Chunk freelists:");
|
||||
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
if (usesCompressedClassSpace) {
|
||||
output.shouldContain("Non-Class:");
|
||||
output.shouldContain("Class:");
|
||||
}
|
||||
output.shouldContain("Virtual space:");
|
||||
output.shouldContain("Chunk freelist");
|
||||
output.shouldContain("Waste");
|
||||
output.shouldMatch("MaxMetaspaceSize:.*201.00.*MB");
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "show-loaders"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldMatch("ClassLoaderData.*for <bootloader>");
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "by-chunktype"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldContain("specialized:");
|
||||
output.shouldContain("small:");
|
||||
output.shouldContain("medium:");
|
||||
output.shouldContain("humongous:");
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "vslist"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldContain("Virtual space list");
|
||||
output.shouldMatch("node.*reserved.*committed.*used.*");
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "vsmap"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldContain("Virtual space map:");
|
||||
output.shouldContain("HHHHHHHHHHH");
|
||||
|
||||
// Test with different scales
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=G"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldMatch("MaxMetaspaceSize:.*0.2.*GB");
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=K"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldMatch("MaxMetaspaceSize:.*205824.00 KB");
|
||||
|
||||
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=1"});
|
||||
output = new OutputAnalyzer(pb.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldMatch("MaxMetaspaceSize:.*210763776 bytes");
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
boolean testForCompressedClassSpace = false;
|
||||
if (args[0].equals("with-compressed-class-space")) {
|
||||
testForCompressedClassSpace = true;
|
||||
} else if (args[0].equals("without-compressed-class-space")) {
|
||||
testForCompressedClassSpace = false;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid argument: " + args[0]);
|
||||
}
|
||||
doTheTest(testForCompressedClassSpace);
|
||||
}
|
||||
}
|
||||
@ -46,6 +46,10 @@ public class PrintNMTStatistics {
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
output_detail.shouldContain("Virtual memory map:");
|
||||
output_detail.shouldContain("Details:");
|
||||
|
||||
// PrintNMTStatistics also prints out metaspace statistics as a convenience.
|
||||
output_detail.shouldContain("Metaspace:");
|
||||
|
||||
output_detail.shouldHaveExitValue(0);
|
||||
|
||||
// Make sure memory reserved for Module processing is recorded.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user