Skip to content

Commit 6e2d3c6

Browse files
committed
8302455: VM.classloader_stats memory size values are wrong
Reviewed-by: coleenp, dholmes
1 parent 1480d41 commit 6e2d3c6

File tree

4 files changed

+61
-26
lines changed

4 files changed

+61
-26
lines changed

src/hotspot/share/classfile/classLoaderStats.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) {
8484

8585
ClassLoaderMetaspace* ms = cld->metaspace_or_null();
8686
if (ms != nullptr) {
87-
size_t used_bytes, capacity_bytes;
88-
ms->calculate_jfr_stats(&used_bytes, &capacity_bytes);
87+
size_t used_words, capacity_words;
88+
ms->usage_numbers(&used_words, nullptr, &capacity_words);
89+
size_t used_bytes = used_words * BytesPerWord;
90+
size_t capacity_bytes = capacity_words * BytesPerWord;
8991
if(cld->has_class_mirror_holder()) {
9092
cls->_hidden_chunk_sz += capacity_bytes;
9193
cls->_hidden_block_sz += used_bytes;
@@ -98,7 +100,6 @@ void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) {
98100
}
99101
}
100102

101-
102103
// Handles the difference in pointer width on 32 and 64 bit platforms
103104
#ifdef _LP64
104105
#define SPACE "%8s"

src/hotspot/share/memory/classLoaderMetaspace.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,24 +160,30 @@ void ClassLoaderMetaspace::verify() const {
160160
}
161161
#endif // ASSERT
162162

163-
// This only exists for JFR and jcmd VM.classloader_stats. We may want to
164-
// change this. Capacity as a stat is of questionable use since it may
165-
// contain committed and uncommitted areas. For now we do this to maintain
166-
// backward compatibility with JFR.
167-
void ClassLoaderMetaspace::calculate_jfr_stats(size_t* p_used_bytes, size_t* p_capacity_bytes) const {
168-
// Implement this using the standard statistics objects.
169-
size_t used_c = 0, cap_c = 0, used_nc = 0, cap_nc = 0;
170-
if (non_class_space_arena() != nullptr) {
171-
non_class_space_arena()->usage_numbers(&used_nc, nullptr, &cap_nc);
163+
// Convenience method to get the most important usage statistics.
164+
void ClassLoaderMetaspace::usage_numbers(Metaspace::MetadataType mdType, size_t* p_used_words,
165+
size_t* p_committed_words, size_t* p_capacity_words) const {
166+
const MetaspaceArena* arena = (mdType == Metaspace::MetadataType::ClassType) ?
167+
class_space_arena() : non_class_space_arena();
168+
arena->usage_numbers(p_used_words, p_committed_words, p_capacity_words);
169+
}
170+
171+
// Convenience method to get total usage numbers
172+
void ClassLoaderMetaspace::usage_numbers(size_t* p_used_words, size_t* p_committed_words,
173+
size_t* p_capacity_words) const {
174+
size_t used_nc, comm_nc, cap_nc;
175+
usage_numbers(Metaspace::MetadataType::NonClassType, &used_nc, &comm_nc, &cap_nc);
176+
size_t used_c = 0, comm_c = 0, cap_c = 0;
177+
if (Metaspace::using_class_space()) {
178+
usage_numbers(Metaspace::MetadataType::ClassType, &used_c, &comm_c, &cap_c);
172179
}
173-
if (class_space_arena() != nullptr) {
174-
class_space_arena()->usage_numbers(&used_c, nullptr, &cap_c);
180+
if (p_used_words != nullptr) {
181+
(*p_used_words) = used_nc + used_c;
175182
}
176-
if (p_used_bytes != nullptr) {
177-
*p_used_bytes = used_c + used_nc;
183+
if (p_committed_words != nullptr) {
184+
(*p_committed_words) = comm_nc + comm_c;
178185
}
179-
if (p_capacity_bytes != nullptr) {
180-
*p_capacity_bytes = cap_c + cap_nc;
186+
if (p_capacity_words != nullptr) {
187+
(*p_capacity_words) = cap_nc + cap_c;
181188
}
182189
}
183-

src/hotspot/share/memory/classLoaderMetaspace.hpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,15 @@ class ClassLoaderMetaspace : public CHeapObj<mtClass> {
9999

100100
DEBUG_ONLY(void verify() const;)
101101

102-
// This only exists for JFR and jcmd VM.classloader_stats. We may want to
103-
// change this. Capacity as a stat is of questionable use since it may
104-
// contain committed and uncommitted areas. For now we do this to maintain
105-
// backward compatibility with JFR.
106-
void calculate_jfr_stats(size_t* p_used_bytes, size_t* p_capacity_bytes) const;
102+
// Convenience method to get the most important usage statistics for either class
103+
// or non-class space. For more detailed statistics, use add_to_statistics().
104+
void usage_numbers(Metaspace::MetadataType mdType, size_t* p_used_words,
105+
size_t* p_committed_words, size_t* p_capacity_words) const;
106+
107+
// Convenience method to get the most important usage statistics (totals; both class- and non-class spaces)
108+
// For more detailed statistics, use add_to_statistics().
109+
void usage_numbers(size_t* p_used_words, size_t* p_committed_words,
110+
size_t* p_capacity_words) const;
107111

108112
}; // end: ClassLoaderMetaspace
109113

test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
* @run testng/othervm --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest
3333
*/
3434

35+
/*
36+
* @test
37+
* @summary Test of diagnostic command VM.classloader_stats (-UseCCP)
38+
* @library /test/lib
39+
* @modules java.base/jdk.internal.misc
40+
* java.compiler
41+
* java.management
42+
* jdk.internal.jvmstat/sun.jvmstat.monitor
43+
* @run testng/othervm -XX:-UseCompressedClassPointers --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest
44+
*/
45+
3546
import org.testng.annotations.Test;
3647
import org.testng.Assert;
3748

@@ -80,6 +91,7 @@ public void run(CommandExecutor executor) throws ClassNotFoundException {
8091
}
8192

8293
OutputAnalyzer output = executor.execute("VM.classloader_stats");
94+
output.reportDiagnosticSummary();
8395
Iterator<String> lines = output.asLines().iterator();
8496
while (lines.hasNext()) {
8597
String line = lines.next();
@@ -91,8 +103,20 @@ public void run(CommandExecutor executor) throws ClassNotFoundException {
91103
if (!m.group(1).equals("1")) {
92104
Assert.fail("Should have loaded 1 class: " + line);
93105
}
94-
checkPositiveInt(m.group(2));
95-
checkPositiveInt(m.group(3));
106+
107+
long capacityBytes = Long.parseLong(m.group(2)); // aka "Chunksz"
108+
long usedBytes = Long.parseLong(m.group(3)); // aka "Blocksz"
109+
110+
// Minimum expected sizes: initial capacity is governed by the chunk size of the first chunk, which
111+
// depends on the arena growth policy. Since this is a normal class loader, we expect as initial chunk
112+
// size at least 4k (if UseCompressedClassPointers is off).
113+
// Minimum used size is difficult to guess but should be at least 1k.
114+
// Maximum expected sizes: We just assume a reasonable maximum. We only loaded one class, so
115+
// we should not see values > 64k.
116+
long K = 1024;
117+
if (capacityBytes < (K * 4) || usedBytes < K || capacityBytes > (64 * K) || usedBytes > (64 * K)) {
118+
throw new RuntimeException("Sizes seem off. Chunksz: " + capacityBytes + ", Blocksz: " + usedBytes);
119+
}
96120

97121
String next = lines.next();
98122
System.out.println("DummyClassLoader next: " + next);

0 commit comments

Comments
 (0)