This guide highlights common footguns when writing JVM agents.
- Enabling events without the required capability yields silent failures.
- Calling JNI from
GarbageCollectionStart/Finishis forbidden. - Holding JVM monitors while calling JVMTI can deadlock the VM.
JNIEnvis thread-local and invalid on other threads.- Callback order can differ across JVM implementations.
ClassFileLoadHookcan be called concurrently and very early.- Allocating memory in hot callbacks can cause severe pauses.
- Mis-sized
new_class_data_lencorrupts class loading. - JVMTI buffers must be deallocated with
Deallocate, notfree. - Some events are disabled by default for performance reasons.
- A newer Rust table layout does not make a newer slot callable on an older JVM; use the version-gated wrappers.
- Value-object allocation callbacks may provide a null
jobjecteven when class and size are valid. export_agent!only exportsAgent_OnLoad/OnAttach/OnUnload. It does not register callbacks or enable events;vm_initandvm_deathstay silent until you callset_default_agent_callbacksandenable_vm_lifecycle_events(or the equivalent).Jvmti::dispose_environmentisunsafe. Consuming one wrapper does not prove callbacks for that environment have drained.ClassFileLoadHookreplacements go throughset_transformed_class. Writing the output pointers by hand can leak, and a later panic in the same callback rolls the pending buffer back instead of committing it.NativeMethodBindis an in/out address. Absent and no-op handlers must leave the VM-selected implementation unchanged;set_new_addressisunsafeand requires an exact JNI ABI match.- High-level
JniEnvcall helpers do not turn a pending Java exception intoResult. After a JNI call that can throw, check for a pending exception before invoking operations that do not permit one, then propagate, report, or clear it deliberately.