Skip to content

support classloader metaspace stat#3188

Merged
hengyunabc merged 4 commits into
masterfrom
classloader-metaspace
May 8, 2026
Merged

support classloader metaspace stat#3188
hengyunabc merged 4 commits into
masterfrom
classloader-metaspace

Conversation

@hengyunabc
Copy link
Copy Markdown
Collaborator

No description provided.

@hengyunabc hengyunabc requested a review from Copilot May 8, 2026 07:12
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69f587f89a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

event.getLong("classCount"),
event.getLong("chunkSize"),
event.getLong("blockSize"),
event.getLong("hiddenBlockSize"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Support pre-17 JFR classloader statistics fields

StatsRow.from unconditionally reads hiddenBlockSize, but jdk.ClassLoaderStatistics on older supported runtimes (e.g. JDK 11) does not expose that field, so event.getLong("hiddenBlockSize") throws at runtime and the whole classloader-metaspace command fails. Because the command is registered whenever JFR is present, this turns into a hard failure on those JDKs instead of degraded output; the parser should tolerate version differences (for example, fallback to unsafeAnonymousBlockSize or 0).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR introduces a new classloader-metaspace command to report metaspace / class metadata memory per ClassLoader using JFR, along with terminal rendering, tests, and documentation.

Changes:

  • Add classloader-metaspace command implementation backed by JFR jdk.ClassLoaderStatistics
  • Add result model + view, register them in the command pack and view resolver
  • Add English/Chinese docs and unit tests for parsing/sorting and table rendering

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
site/docs/en/doc/commands.md Adds the new command to the English command index
site/docs/en/doc/classloader-metaspace.md New English documentation page for classloader-metaspace
site/docs/doc/commands.md Adds the new command to the Chinese command index
site/docs/doc/classloader-metaspace.md New Chinese documentation page for classloader-metaspace
core/src/test/java/com/taobao/arthas/core/command/view/ClassLoaderMetaspaceViewTest.java Tests rendering for default/verbose columns and narrow terminals
core/src/test/java/com/taobao/arthas/core/command/klass100/ClassLoaderMetaspaceCommandTest.java Tests time parsing, display-name fallback, sorting/limit behavior
core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java Registers the new view for rendering
core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderMetaspaceView.java Implements terminal table rendering for the command output
core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderMetaspaceModel.java Adds a structured model for classloader-metaspace output
core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderMetaspaceCommand.java Implements the new command using JFR recording + mapping event
core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java Registers the new command when JFR is available

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +22
| Name | Specification |
| --------------------: | :-------------------------------------------------------------------------------------------------------------- |
| `[c:]` | ClassLoader hashcode, same format as `classloader -c` |
| `[classLoaderClass:]` | Filter by full ClassLoader class name |
| `[duration:]` | JFR sampling duration, `2500ms` by default; supports `ms`, `s`, `m`; a plain number means milliseconds |
| `[period:]` | `jdk.ClassLoaderStatistics` period, `500ms` by default; supports `ms`, `s`, `m` |
| `[limit:]` | Show only the first N rows after sorting by `chunkSize` descending |
| `[verbose:]` | Show full diagnostic columns, including `classLoaderData`, `hiddenBlockSize` and `type`; `-v` is also supported |
Comment on lines +15 to +22
| 参数名称 | 参数说明 |
| --------------------: | :--------------------------------------------------------------------------------- |
| `[c:]` | ClassLoader 的 hashcode,格式和 `classloader -c` 一致 |
| `[classLoaderClass:]` | 按 ClassLoader 完整类名过滤 |
| `[duration:]` | JFR 采样时长,默认 `2500ms`,支持 `ms`、`s`、`m`,裸数字按毫秒处理 |
| `[period:]` | `jdk.ClassLoaderStatistics` 采样周期,默认 `500ms`,支持 `ms`、`s`、`m` |
| `[limit:]` | 只输出按 `chunkSize` 降序排序后的前 N 行 |
| `[verbose:]` | 输出完整诊断列,包括 `classLoaderData`、`hiddenBlockSize` 和 `type`;也可使用 `-v` |
Comment on lines +347 to +353
long millis = Long.parseLong(text) * multiplier;
if (millis <= 0) {
throw new IllegalArgumentException(optionName + " must be greater than 0.");
}
return millis;
} catch (NumberFormatException e) {
throw new IllegalArgumentException(optionName + " is invalid: " + value);
Comment on lines +435 to +450
private static StatsRow from(RecordedEvent event) {
RecordedClassLoader loader = event.getValue("classLoader");
long loaderId = loader == null ? 0 : loader.getId();
String name = loader == null ? "BootstrapClassLoader" : loader.getName();
return new StatsRow(
event.getStartTime(),
loaderId,
loader == null ? "null" : null,
name,
typeName(loader),
event.getLong("classLoaderData"),
event.getLong("classCount"),
event.getLong("chunkSize"),
event.getLong("blockSize"),
event.getLong("hiddenBlockSize"));
}
Comment on lines +224 to +250
private RecordingData readRecording(Path recording) throws IOException {
Map<Long, LoaderMapping> mappingByLoaderId = new HashMap<Long, LoaderMapping>();
Map<Long, StatsRow> latestStatsByLoaderId = new HashMap<Long, StatsRow>();
RecordingFile file = new RecordingFile(recording);
try {
while (file.hasMoreEvents()) {
RecordedEvent event = file.readEvent();
String eventName = event.getEventType().getName();
if (MAPPING_EVENT_NAME.equals(eventName)) {
RecordedClass anchorClass = event.getClass("anchorClass");
RecordedClassLoader loader = anchorClass == null ? null : anchorClass.getClassLoader();
if (loader != null) {
mappingByLoaderId.put(loader.getId(), LoaderMapping.from(event));
}
} else if (STATS_EVENT_NAME.equals(eventName)) {
StatsRow row = StatsRow.from(event);
StatsRow previous = latestStatsByLoaderId.get(row.loaderId);
if (previous == null || row.startTime.isAfter(previous.startTime)) {
latestStatsByLoaderId.put(row.loaderId, row);
}
}
}
} finally {
file.close();
}
return new RecordingData(mappingByLoaderId, latestStatsByLoaderId.values());
}
@hengyunabc hengyunabc force-pushed the classloader-metaspace branch from 5daa20e to 0d100c2 Compare May 8, 2026 07:53
@hengyunabc hengyunabc merged commit 41bafa7 into master May 8, 2026
1 check passed
@hengyunabc hengyunabc deleted the classloader-metaspace branch May 8, 2026 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants