SecuredDataAccessLogger scheduled this::scheduledFlush on its worker thread and registered new Thread(this::close) as a JVM shutdown hook. A live thread and the shutdown-hook registry are GC roots, so each logger, its runtime and its whole QInstance stayed reachable for the life of the JVM. Hosts that build many instances (test suites, instance reloads) leaked one instance per build. kof22 had to stop the worker and remove the hook by reflection.
Fix
The worker task (WeakTick) and the hook (WeakClose) hold the logger through a WeakReference. A dropped instance can be collected; the worker shuts itself down at its next tick once the logger is gone. A live logger behaves exactly as before: it drains every tick, and the hook drains at JVM shutdown.
SecuredDataQBitConfig.withRegisterShutdownHook(boolean) (default true): hosts that call SecuredDataRuntime.close() from their own shutdown sequence can skip the hook.
SecuredDataAccessLogger.isWorkerRunning().
README (Logging and lifecycle, Configuration) and CHANGELOG (which also lists #18).
Tests
aDiscardedInstanceCanBeCollected: builds an instance with a 10 ms worker, drops it, and asserts it is collected. It fails with the old strong references (verified by reverting the two lines) and passes now.
shutdownHookIsOptional: no hook; close still drains and stops the worker.
Follow-up to #18, found installing 0.1.0-RC.2 in kof22 (KofTwentyTwo/Website-Backend#297, whose CI ran out of heap).
## Problem
`SecuredDataAccessLogger` scheduled `this::scheduledFlush` on its worker thread and registered `new Thread(this::close)` as a JVM shutdown hook. A live thread and the shutdown-hook registry are GC roots, so each logger, its runtime and its whole `QInstance` stayed reachable for the life of the JVM. Hosts that build many instances (test suites, instance reloads) leaked one instance per build. kof22 had to stop the worker and remove the hook by reflection.
## Fix
- The worker task (`WeakTick`) and the hook (`WeakClose`) hold the logger through a `WeakReference`. A dropped instance can be collected; the worker shuts itself down at its next tick once the logger is gone. A live logger behaves exactly as before: it drains every tick, and the hook drains at JVM shutdown.
- `SecuredDataQBitConfig.withRegisterShutdownHook(boolean)` (default `true`): hosts that call `SecuredDataRuntime.close()` from their own shutdown sequence can skip the hook.
- `SecuredDataAccessLogger.isWorkerRunning()`.
- README (Logging and lifecycle, Configuration) and CHANGELOG (which also lists #18).
## Tests
- `aDiscardedInstanceCanBeCollected`: builds an instance with a 10 ms worker, drops it, and asserts it is collected. It fails with the old strong references (verified by reverting the two lines) and passes now.
- `shutdownHookIsOptional`: no hook; close still drains and stops the worker.
`./mvnw verify` passes locally (checkstyle, JaCoCo).
With #18 and this PR in an RC, kof22 can drop both of its workarounds (the virtual-field resolver wrapper and the reflective worker stop).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up to #18, found installing 0.1.0-RC.2 in kof22 (KofTwentyTwo/Website-Backend#297, whose CI ran out of heap).
Problem
SecuredDataAccessLoggerscheduledthis::scheduledFlushon its worker thread and registerednew Thread(this::close)as a JVM shutdown hook. A live thread and the shutdown-hook registry are GC roots, so each logger, its runtime and its wholeQInstancestayed reachable for the life of the JVM. Hosts that build many instances (test suites, instance reloads) leaked one instance per build. kof22 had to stop the worker and remove the hook by reflection.Fix
WeakTick) and the hook (WeakClose) hold the logger through aWeakReference. A dropped instance can be collected; the worker shuts itself down at its next tick once the logger is gone. A live logger behaves exactly as before: it drains every tick, and the hook drains at JVM shutdown.SecuredDataQBitConfig.withRegisterShutdownHook(boolean)(defaulttrue): hosts that callSecuredDataRuntime.close()from their own shutdown sequence can skip the hook.SecuredDataAccessLogger.isWorkerRunning().Tests
aDiscardedInstanceCanBeCollected: builds an instance with a 10 ms worker, drops it, and asserts it is collected. It fails with the old strong references (verified by reverting the two lines) and passes now.shutdownHookIsOptional: no hook; close still drains and stops the worker../mvnw verifypasses locally (checkstyle, JaCoCo).With #18 and this PR in an RC, kof22 can drop both of its workarounds (the virtual-field resolver wrapper and the reflective worker stop).
🤖 Generated with Claude Code