Skip to content

Commit 2301a42

Browse files
feat(introspection): v26.4-Alpha.4 - JVM introspection API (ThreadInsight/ClassStats/HeapSummary/GcSummary/CompilationSummary + JvmInsight facade over MXBeans; 9 tests, 526 total)
1 parent eacfd2f commit 2301a42

14 files changed

Lines changed: 485 additions & 3 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Aprism
22

33
> A cross-platform, cross-edition, cross-version Minecraft mod loader and injector compatible with JE/BE.
4-
> Author: BlockConnect@StarsailsClover | Version: v26.4-Alpha.3 (Phase0 internal)
4+
> Author: BlockConnect@StarsailsClover | Version: v26.4-Alpha.4 (Phase0 internal)
55
66
Aprism Loader is a unified mod loading framework that supports JE Fabric, Forge, NeoForge, Quilt and LiteLoader modpacks through Aprism Extensions (.aep, provided by the AprismRefract sub-project), brings a Fabric-like mod loader ecosystem to Bedrock Edition (BE) via native injection, and provides rapid JE-to-BE mod conversion tooling. JE Aprism Native is a modern, JVM-based foundation that forces unified interfaces with a monotonic version contract (only increases, never decreases). The Aprism core itself is native-only: loader support lives outside the core behind the LoaderEntrypointHandler SPI.
77

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.aprism.api.introspection;
2+
3+
/**
4+
* A snapshot of class-loading statistics (v26.4-Alpha.4, JVM
5+
* introspection API).
6+
*
7+
* @param loadedClassCount classes currently loaded
8+
* @param unloadedClassCount classes unloaded since startup
9+
* @param totalLoadedClassCount classes loaded since startup
10+
* @author BlockConnect@StarsailsClover
11+
*/
12+
public record ClassStats(long loadedClassCount, long unloadedClassCount,
13+
long totalLoadedClassCount) {
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.aprism.api.introspection;
2+
3+
/**
4+
* A snapshot of the JIT compiler state (v26.4-Alpha.4, JVM introspection
5+
* API).
6+
*
7+
* @param compilerName the JIT compiler name ({@code null} when no
8+
* compiler is available)
9+
* @param totalCompileTimeMs approximate accumulated compilation time in
10+
* milliseconds ({@code -1} if unsupported)
11+
* @param jitAvailable whether a JIT compiler is present in this VM
12+
* @author BlockConnect@StarsailsClover
13+
*/
14+
public record CompilationSummary(String compilerName, long totalCompileTimeMs,
15+
boolean jitAvailable) {
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.aprism.api.introspection;
2+
3+
/**
4+
* A snapshot of one garbage collector's activity (v26.4-Alpha.4, JVM
5+
* introspection API).
6+
*
7+
* @param name the collector name
8+
* @param collectionCount the number of collections performed ({@code -1}
9+
* if undefined)
10+
* @param collectionTimeMs approximate accumulated collection time in
11+
* milliseconds ({@code -1} if undefined)
12+
* @author BlockConnect@StarsailsClover
13+
*/
14+
public record GcSummary(String name, long collectionCount, long collectionTimeMs) {
15+
16+
/**
17+
* Canonical compact constructor: validates the name.
18+
*/
19+
public GcSummary {
20+
if (name == null || name.isBlank()) {
21+
throw new IllegalArgumentException("collector name must be non-blank");
22+
}
23+
}
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.aprism.api.introspection;
2+
3+
/**
4+
* A snapshot of heap and non-heap memory usage (v26.4-Alpha.4, JVM
5+
* introspection API). All values are in bytes; {@code -1} means the value
6+
* is undefined for this VM.
7+
*
8+
* @param heapUsed heap bytes currently used
9+
* @param heapCommitted heap bytes committed
10+
* @param heapMax maximum heap bytes ({@code -1} if undefined)
11+
* @param nonHeapUsed non-heap bytes currently used
12+
* @author BlockConnect@StarsailsClover
13+
*/
14+
public record HeapSummary(long heapUsed, long heapCommitted, long heapMax, long nonHeapUsed) {
15+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.aprism.api.introspection;
2+
3+
import java.util.List;
4+
5+
/**
6+
* JVM introspection surface (v26.4-Alpha.4): a typed, stable view over
7+
* thread stacks, class statistics, heap usage, GC activity, JIT state and
8+
* VM identity. This is the loader-level foundation the AprismateAgent
9+
* performance work builds on; it intentionally reads through
10+
* {@code ManagementFactory} MXBeans so it works on any compliant JVM
11+
* (including AprismJDK, where deeper seams may later replace it).
12+
*
13+
* @author BlockConnect@StarsailsClover
14+
*/
15+
public interface JvmInsight {
16+
17+
/**
18+
* @return insights for all live threads at this instant
19+
*/
20+
List<ThreadInsight> threads();
21+
22+
/**
23+
* @return current class-loading statistics
24+
*/
25+
ClassStats classStats();
26+
27+
/**
28+
* @return current heap and non-heap usage
29+
*/
30+
HeapSummary heap();
31+
32+
/**
33+
* @return activity snapshots of all registered garbage collectors
34+
*/
35+
List<GcSummary> gcCollectors();
36+
37+
/**
38+
* @return the JIT compiler state
39+
*/
40+
CompilationSummary compilation();
41+
42+
/**
43+
* @return VM uptime in milliseconds
44+
*/
45+
long uptimeMs();
46+
47+
/**
48+
* @return the VM name (e.g. {@code OpenJDK 64-Bit Server VM})
49+
*/
50+
String vmName();
51+
52+
/**
53+
* @return the VM vendor
54+
*/
55+
String vmVendor();
56+
57+
/**
58+
* @return the Java runtime version
59+
*/
60+
String javaVersion();
61+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.aprism.api.introspection;
2+
3+
import java.util.List;
4+
5+
/**
6+
* A snapshot of a single live thread (v26.4-Alpha.4, JVM introspection
7+
* API). Provides the loader-level with a typed view of thread state
8+
* without JMX reflection.
9+
*
10+
* @param threadId the unique thread id
11+
* @param name the thread name
12+
* @param state the thread state name (e.g. {@code RUNNABLE}, {@code WAITING})
13+
* @param stackDepth the current stack depth
14+
* @param topFrames the topmost stack frame descriptions (may be empty)
15+
* @author BlockConnect@StarsailsClover
16+
*/
17+
public record ThreadInsight(long threadId, String name, String state, int stackDepth,
18+
List<String> topFrames) {
19+
20+
/**
21+
* Canonical compact constructor: defensive copies.
22+
*/
23+
public ThreadInsight {
24+
if (name == null || name.isBlank()) {
25+
throw new IllegalArgumentException("thread name must be non-blank");
26+
}
27+
if (state == null) {
28+
throw new IllegalArgumentException("thread state must be non-null");
29+
}
30+
topFrames = topFrames == null ? List.of() : List.copyOf(topFrames);
31+
}
32+
}

‎aprism-loader-core/src/main/java/com/aprism/loader/AprismRuntime.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import com.aprism.loader.networking.NetworkingRegistry;
5050
import com.aprism.loader.rendering.RenderingRegistry;
5151
import com.aprism.loader.registry.GameRegistries;
52+
import com.aprism.api.introspection.JvmInsight;
53+
import com.aprism.loader.introspection.JvmInsightImpl;
5254
import com.aprism.loader.settings.SettingsRegistry;
5355
import com.aprism.loader.modmenu.ModListEntry;
5456
import com.aprism.loader.modmenu.ModListRegistry;
@@ -116,6 +118,8 @@ public final class AprismRuntime {
116118
private final ModListRegistry modListRegistry = new ModListRegistry();
117119
/** Per-mod settings registry (v26.2-Alpha.3, goal #7 part 2). */
118120
private final SettingsRegistry settingsRegistry = new SettingsRegistry();
121+
/** JVM introspection API (v26.4-Alpha.4). */
122+
private final JvmInsightImpl jvmInsight = new JvmInsightImpl();
119123
/** Game-event dispatcher (v26.3-Alpha.1, QA0 gap #1). */
120124
private GameEventDispatcher gameEventDispatcher;
121125
/** Typed game-content registries (v26.3-Alpha.2, QA0 gap #2). */
@@ -465,6 +469,14 @@ public SettingsRegistry getSettings() {
465469
return settingsRegistry;
466470
}
467471

472+
/**
473+
* @return the JVM introspection surface (v26.4-Alpha.4): threads,
474+
* class stats, heap, GC, JIT and VM identity
475+
*/
476+
public JvmInsight getJvmInsight() {
477+
return jvmInsight;
478+
}
479+
468480
/**
469481
* Rebuilds the mod list registry from the current runtime state
470482
* (v26.2-Alpha.2, goal #7). Every loaded extension and mod contributes a
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.aprism.loader.introspection;
2+
3+
import com.aprism.api.introspection.ClassStats;
4+
import com.aprism.api.introspection.CompilationSummary;
5+
import com.aprism.api.introspection.GcSummary;
6+
import com.aprism.api.introspection.HeapSummary;
7+
import com.aprism.api.introspection.JvmInsight;
8+
import com.aprism.api.introspection.ThreadInsight;
9+
10+
import java.lang.management.ClassLoadingMXBean;
11+
import java.lang.management.CompilationMXBean;
12+
import java.lang.management.GarbageCollectorMXBean;
13+
import java.lang.management.ManagementFactory;
14+
import java.lang.management.MemoryMXBean;
15+
import java.lang.management.MemoryUsage;
16+
import java.lang.management.RuntimeMXBean;
17+
import java.lang.management.ThreadInfo;
18+
import java.lang.management.ThreadMXBean;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
/**
23+
* Implementation of {@link JvmInsight} over the standard
24+
* {@code ManagementFactory} MXBeans (v26.4-Alpha.4). Works on any
25+
* compliant JVM; on AprismJDK deeper seams may replace individual
26+
* methods later without changing this contract.
27+
*
28+
* @author BlockConnect@StarsailsClover
29+
*/
30+
public final class JvmInsightImpl implements JvmInsight {
31+
32+
/** Maximum number of stack frames captured per thread. */
33+
private static final int MAX_FRAMES = 16;
34+
35+
@Override
36+
public List<ThreadInsight> threads() {
37+
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
38+
ThreadInfo[] infos = threadBean.dumpAllThreads(false, false);
39+
List<ThreadInsight> result = new ArrayList<>(infos.length);
40+
for (ThreadInfo info : infos) {
41+
if (info == null) {
42+
continue;
43+
}
44+
StackTraceElement[] stack = info.getStackTrace();
45+
List<String> frames = new ArrayList<>();
46+
int limit = Math.min(stack.length, MAX_FRAMES);
47+
for (int i = 0; i < limit; i++) {
48+
frames.add(stack[i].toString());
49+
}
50+
result.add(new ThreadInsight(info.getThreadId(), info.getThreadName(),
51+
info.getThreadState().name(), stack.length, frames));
52+
}
53+
return result;
54+
}
55+
56+
@Override
57+
public ClassStats classStats() {
58+
ClassLoadingMXBean bean = ManagementFactory.getClassLoadingMXBean();
59+
return new ClassStats(bean.getLoadedClassCount(), bean.getUnloadedClassCount(),
60+
bean.getTotalLoadedClassCount());
61+
}
62+
63+
@Override
64+
public HeapSummary heap() {
65+
MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
66+
MemoryUsage heapUsage = bean.getHeapMemoryUsage();
67+
MemoryUsage nonHeapUsage = bean.getNonHeapMemoryUsage();
68+
return new HeapSummary(heapUsage.getUsed(), heapUsage.getCommitted(),
69+
heapUsage.getMax(), nonHeapUsage.getUsed());
70+
}
71+
72+
@Override
73+
public List<GcSummary> gcCollectors() {
74+
List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
75+
List<GcSummary> result = new ArrayList<>(beans.size());
76+
for (GarbageCollectorMXBean bean : beans) {
77+
result.add(new GcSummary(bean.getName(), bean.getCollectionCount(),
78+
bean.getCollectionTime()));
79+
}
80+
return result;
81+
}
82+
83+
@Override
84+
public CompilationSummary compilation() {
85+
CompilationMXBean bean = ManagementFactory.getCompilationMXBean();
86+
if (bean == null) {
87+
return new CompilationSummary(null, -1, false);
88+
}
89+
long time = bean.isCompilationTimeMonitoringSupported()
90+
? bean.getTotalCompilationTime() : -1;
91+
return new CompilationSummary(bean.getName(), time, true);
92+
}
93+
94+
@Override
95+
public long uptimeMs() {
96+
RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
97+
return bean.getUptime();
98+
}
99+
100+
@Override
101+
public String vmName() {
102+
return ManagementFactory.getRuntimeMXBean().getVmName();
103+
}
104+
105+
@Override
106+
public String vmVendor() {
107+
return ManagementFactory.getRuntimeMXBean().getVmVendor();
108+
}
109+
110+
@Override
111+
public String javaVersion() {
112+
return System.getProperty("java.version");
113+
}
114+
}

0 commit comments

Comments
 (0)