Metrics.java

package lab.sample;

/**
 * Stage measurements: elapsed time and memory in use.
 *
 * <p>Deliberately founded on the JDK alone ({@code System.nanoTime}, {@code Runtime}): these
 * figures serve as an <b>independent reference</b>. When an analysis tool announces a time or
 * a footprint, one wants to be able to confront it with a measurement that does not come from
 * it — and note in passing how much the tool itself slows the run down.
 *
 * <p>Memory is read after a call to {@code System.gc()}: without it, one mostly measures the
 * garbage not yet collected, which means nothing. The call remains a <em>suggestion</em> made
 * to the JVM, never a guarantee — hence the order of magnitude announced rather than an exact
 * value.
 */
public final class Metrics {

    private final long startNanos = System.nanoTime();
    private long lastNanos = startNanos;

    void stage(String label) {
        long now = System.nanoTime();
        double sinceLastMs = (now - lastNanos) / 1_000_000.0;
        double totalMs = (now - startNanos) / 1_000_000.0;
        lastNanos = now;

        System.gc();
        Runtime runtime = Runtime.getRuntime();
        long usedMb = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
        long maxMb = runtime.maxMemory() / (1024 * 1024);

        System.out.printf("  [%-28s] stage %8.0f ms | total %8.0f ms | memory ~%d MB / %d MB max%n",
                label, sinceLastMs, totalMs, usedMb, maxMb);
    }
}