Troubleshooting
The asymm CLI
Eliya ships a small diagnostic helper at /usr/bin/asymm (symlinked from ${JAVA_HOME}/bin/asymm by RPM/DEB postinstall scripts; available directly at ${JAVA_HOME}/bin/asymm on tar.gz installs). Use it to confirm which runtime you're on and to print build metadata.
The first confirms the active JVM is Eliya. The second prints the six Phase 1 ergonomics with their values and origins (look for {ergonomic} in the origin column on flags set by the profile). The third confirms the diagnostic-path root is writable. Attach these three outputs to any issue you file.
Full CLI reference: asymm CLI.
Common issues
TLS handshake fails with "protocol disabled" or "no cipher suites in common"
Upstream JDK 25 disables SSLv3, TLS 1.0, TLS 1.1, and weak cipher suites; Eliya inherits this unchanged. A legacy peer requiring these protocols will fail to negotiate. Either upgrade the peer (correct fix) or override with:
The double == overrides; a single = appends. Use only when legacy interoperability is genuinely required.
"Unrecognized VM option 'EliyaProfile'"
Three possible causes:
1. Non-Eliya JVM. You're running a non-Eliya OpenJDK distribution. Confirm with java -version; the version string must include "Eliya". If not, check JAVA_HOME, your PATH, and SDKman's current version (sdk current java).
2. Pre-rename JVM args. If you're migrating from a pre-public-release build of Eliya, the flag form changed in May 2026:
- Old:
-XX:+UseEliyaDefaults(boolean toggle) - New:
-XX:EliyaProfile=Production(enum-value selector)
The rename is complete; Phase 1 ships only the EliyaProfile form. Scripts or documents referencing -XX:+UseEliyaDefaults predate the public release and need updating.
3. Pre-Phase-1 build. If you're running an alpha/preview Eliya build that predates the May 2026 strategy revision, upgrade to the Phase 1 GA release. Phase 1 ships the EliyaProfile flag declaration; earlier internal builds may not.
Verify which case applies:
"Profile '<X>' is reserved for Phase 4"
If JVM startup fails with [Eliya] Fatal: Profile '<X>' is reserved for Phase 4. Currently available: None, Production., you've attempted to use a Phase 4 compliance profile in a Phase 1 build.
Phase 1 ships only two profile values:
EliyaProfile=None(default; no Eliya defaults applied)EliyaProfile=Production(observability defaults)
Phase 4 will add compliance-aligned profile values (PCIDSS, HIPAA, SOX, FedRAMP, GDPR, ISO27001, SOC2) plus combined profiles (Healthcare-Payment, Financial-SaaS, etc.) when customer demand justifies. Phase 4 is demand-gated; sign up on the security page to signal demand for a specific framework or combination.
For now, use EliyaProfile=Production to activate Eliya's Phase 1 observability defaults.
SHA256SUMS.txt verification fails
Re-download both the archive and the SHA256SUMS.txt from the same GitHub Release page. If the re-download still fails, do not run the archive; report to security@asymm.systems.
GC pauses are higher than expected
-XX:EliyaProfile=Production does not change GC selection or tuning. JDK 25's automatic ergonomics select G1 for typical server workloads with a 200 ms default pause target. If you need lower pause times, diagnose first with the JFR data Eliya is already recording:
If consistent low pause time is required, opt into Generational ZGC:
See the flags reference for trade-offs between G1, ZGC, and Shenandoah.
SDKman says version not found
Run sdk update to refresh the candidate metadata. If still not found 24 hours after a new Eliya release, check the releases page and file an issue.
Flag conflict detection failed JVM startup
If JVM startup fails with [Eliya] Fatal: ..., a profile flag and an explicit capability negation produced an unrecoverable conflict. Read the error message; it identifies the conflicting flags and suggests resolution.
Common pattern: profile + -XX:-Use<Capability> for a capability the profile requires. Three resolutions:
- Remove the explicit negation
- Drop the profile (
-XX:EliyaProfile=None) and compose capabilities directly - Use a different profile that doesn't require the negated capability
If you're experimenting with flag combinations or running CI matrix tests, disable conflict checking with -XX:-EliyaConflictCheck. Default is checking enabled.
For the full taxonomy and the three-tier conflict response model, see flag architecture.
Heap dump not found at expected path
If -XX:EliyaProfile=Production activates and -XX:+HeapDumpOnOutOfMemoryError fires but the heap dump isn't where you expect, verify the resolved path matches the three-level layout. Paths include service name and (usually) replica name: ${ELIYA_DIAGNOSTIC_PATH}/${service}/${replica}/heap/java_pid<PID>.hprof.
Cross-check the resolved values against the precedence chain (env var, then -Deliya.* system property, then HOSTNAME for service and replica names, then platform default for the base path). If a component resolved unexpectedly (for example, ELIYA_SERVICE_NAME coming from HOSTNAME when you expected it to come from the env var), check whether the env var is set in the JVM process's environment; orchestration platforms sometimes drop env vars that work in shells. If the path does not exist, the JVM creates it on first write; if permissions prevent writing, the heap-dump or JFR call fails with an explicit IO error.
Full path strategy: flags reference Diagnostic paths section.
JFR recordings not appearing
Two common causes:
Path permissions. The JVM process must have write permission to ${ELIYA_DIAGNOSTIC_PATH}/${service}/${replica}/jfr/. For container deployments using the default /var/log/eliya/, ensure the mounted volume is writable by the JVM user. For bare-metal RPM/DEB installs, the JVM service account must be a member of the eliya-diagnostics group (set by package post-install scripts).
Path resolution mismatch. Inspect the JVM process environment (cat /proc/<pid>/environ | tr '\0' '\n' | grep -E "ELIYA_|HOSTNAME") and cross-check against the precedence chain. If the resolved JFR path differs from your expectation, your service or replica resolution chain is producing a different value than you assumed; check whether ELIYA_SERVICE_NAME / ELIYA_REPLICA_NAME are set, or whether HOSTNAME is what you thought.
Multiple replicas writing to the same directory
If /var/log/eliya/${service}/ contains mixed artifacts from multiple replicas without per-replica subdirectories, the replica name resolved to the same value as the service name, triggering the suppression rule. This is intentional for bare-metal multi-JVM hosts (where HOSTNAME is the host, identical for all services) but unintended in container deployments.
Check whether HOSTNAME is being set correctly in your containers (it should be the container ID or pod name, not the host name). If your runtime doesn't set HOSTNAME per container, set ELIYA_REPLICA_NAME explicitly:
Or in Kubernetes, use the downward API:
env:
- name: ELIYA_REPLICA_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name Eliya behaves differently than Corretto / Zulu / Temurin
If -XX:EliyaProfile=Production is set, observability defaults are active: JFR recording to disk, heap dumps configured, GC logs written. Disk write activity will be higher than a vanilla JVM. This is by design; Eliya prioritises diagnostic data availability over filesystem inactivity.
Other behavioural differences should not occur. Eliya inherits upstream OpenJDK 25's Java APIs, JIT, GC, and security configuration unchanged. If you observe an unexpected behavioural difference between Eliya and another OpenJDK 25 distribution, please file an issue with the diagnostic outputs from the top of this page attached: that is a bug we want to know about.
Process memory exceeds JVM heap configuration
If ps shows the JVM process using significantly more memory than -Xmx, the excess is in native memory regions (Metaspace, code cache, thread stacks, DirectByteBuffers, or JNI allocations), not the Java heap.
Eliya's EliyaProfile=Production activates NMT summary mode by default. Capture a baseline early:
Later, after memory has grown, capture the diff:
The output shows which native memory region grew. Common patterns:
- Metaspace growth → classloader leak (frequent in apps that redeploy classes without restart)
- Code cache pressure → JIT compilation pressure, frequent deoptimisation
- Internal / Symbol growth → JVM internal data structures, possibly too many threads
- DirectByteBuffer or JNI growth → application or library leak in native code
For deeper analysis, Phase 2 will bundle Eclipse MAT (headless) and the full async-profiler suite for native allocation tracing. For Phase 1, the NMT summary plus diff is the canonical entry point for native memory diagnosis; APM tools don't have this visibility.
Performance overhead seems higher than expected
Eliya's observability defaults add ~1–2% CPU steady-state overhead. If you observe higher overhead, measure each component.
Baseline benchmark (Eliya without observability):
With Eliya observability:
With JFR specifically disabled (subtraction pattern):
If the JFR-disabled benchmark approaches the baseline, JFR is the dominant cost. For workloads where JFR's overhead is unacceptable (extreme allocation rates, sub-millisecond latency requirements), disable JFR while keeping other Eliya defaults. The flags reference documents the subtraction pattern.
Running Eliya alongside an APM agent
Eliya coexists with APM agents (AppDynamics, Dynatrace, New Relic, Elastic APM, Datadog APM) using the standard -javaagent: mechanism. Two coexistence issues to watch for.
JFR conflict with Datadog Continuous Profiler or similar. Some APM tools also start JFR recordings. If both Eliya and an APM agent try to start JFR recordings, the second will fail with Recording already in progress. Resolutions:
- Let the APM agent manage JFR (disable Eliya's JFR via
-XX:-FlightRecorderafter-XX:EliyaProfile=Production). - Let Eliya manage JFR (disable the APM agent's JFR; consult APM vendor docs).
- Use both but coordinate: APM tools that feed JFR into their own pipeline (Datadog Continuous Profiler) can work with Eliya's JFR configuration; verify with the vendor.
Memory overhead combined. Eliya's observability defaults add ~1–2% CPU and ~13–26 MB memory. APM agent overhead varies by vendor: AppDynamics documents 0–2% CPU and ~10–100 MB plus ~100 MB heap; Dynatrace targets 1–3% CPU and budgets ~200 MB. New Relic, Elastic APM, and Datadog do not publish specific overhead figures. On containers with tight memory limits, measure both independently and combined before committing.
For the full coexistence story, see JVM forensics vs APM.
Looking forward
Phase 2 will expand the troubleshooting toolkit:
- Bundled Eclipse MAT (headless) for offline heap analysis without Internet access, useful in air-gapped environments.
- Bundled async-profiler for hardware-counter and wall-clock profiling beyond what JFR alone provides.
- Wrapping subcommands for the bundled tools, documented in the asymm CLI reference.
Phase 3 will add Asymm Forensics for cross-artifact correlation: analysing JFR + heap dumps + thread dumps + GC logs + crash logs together rather than as separate analyses. Local execution, no SaaS dependency.
Phase 4 will add compliance-aligned profiles (PCI DSS, HIPAA, SOX, FedRAMP, etc.) plus combined profiles for industries needing multiple frameworks.
The Phase 1 troubleshooting flow on this page remains the foundation across all phases. Phase 2/3/4 capabilities are additive.
Filing an issue
Non-security issues: open a GitHub issue on asymmsystems/eliya-jdk. Include the three diagnostic outputs from the top of this page:
asymm --versionjava -XX:EliyaProfile=Production -XX:+PrintFlagsFinal -versionoutput (the six Phase 1 ergonomic flags filtered out)test -w /var/log/eliyaresult (writable check)- OS, architecture, container image if applicable
- Full command line (flags, JAR path)
- Minimal reproducer if possible
Security issues: email security@asymm.systems; see the security page.