hs-web/hsweb-framework · error · IllegalStateException

Object is recycled!

Error message

Object is recycled!

What it means

IllegalStateException raised from OnceRecyclable.get() when the internal recyclable reference has already been nulled out by recycle(). It fires on double-access after recycling: once a once-only Recyclable has been recycled, its object was reset and returned to the pool/destroyed, so any further get() would hand out a stale or repurposed object; the guard rejects that use with this error.

Solutions

  1. Do not call get() after recycle(); re-borrow via recycler.take()/doWith instead
  2. Null out your local reference upon recycle so stale reads can't happen
  3. Avoid keeping ThreadLocalRecyclable references beyond the scope of use
  4. Check for double-recycle bugs in callback chains

Example fix

// before
Recyclable<T> r = recycler.take(true);
T v = r.get();
r.recycle();
T v2 = ((ThreadLocalRecyclable<T>) r).get();
// after
Recyclable<T> r = recycler.take(true);
T v = r.get();
r.recycle();
r = recycler.take(true);
T v2 = r.get();
Defensive patterns

Strategy: type-guard

Validate before calling

T v = recyclable.get(); // capture before recycle

Type guard

static <T> T safeGet(ThreadLocalRecyclable<T> r) {
    try { return r.get(); } catch (IllegalStateException e) { return null; }
}

Try / catch

try {
    return recyclable.get();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("recycled")) return null; // already returned to pool
    throw e;
}

Prevention

When it happens

Trigger: Accessing ThreadLocalRecyclable.get() after recycle() was called on it (the REF handle was set to null). Called from the `ref` accessor path in doWith/take.

Common situations: Storing the recyclable wrapper and reusing it after returning the object to the pool; double-recycle then read; callbacks that run after the object was recycled.

Related errors


AI-assisted analysis of hs-web/hsweb-framework@b2cfc85a57 (2026-09-13). Data as JSON: /api/errors/4884d6375e325795. Report an issue: GitHub.

Appendix: source

Thrown at hsweb-core/src/main/java/org/hswebframework/web/recycler/RecyclerImpl.java:184

        while ((val = queue.poll()) != null) {
            resetAndDestroy(val);
        }
    }

    @AllArgsConstructor
    static class OnceRecyclable<T> implements Recyclable<T> {
        @SuppressWarnings("all")
        static final AtomicReferenceFieldUpdater<OnceRecyclable, Recyclable>
            REF = AtomicReferenceFieldUpdater.newUpdater(OnceRecyclable.class, Recyclable.class, "recyclable");

        private volatile Recyclable<T> recyclable;

        @Override
        public T get() {
            @SuppressWarnings("unchecked")
            Recyclable<T> recyclable = REF.get(this);
            if (recyclable == null) {
                throw new IllegalStateException("Object is recycled!");
            }
            return recyclable.get();
        }

        @Override
        public void recycle() {
            @SuppressWarnings("unchecked")
            Recyclable<T> recyclable = REF.getAndSet(this, null);
            if (recyclable != null) {
                recyclable.recycle();
            }
        }
    }

    @AllArgsConstructor
    static class QueueRecyclable<T> implements Recyclable<T> {
        @SuppressWarnings("all")
        static final AtomicReferenceFieldUpdater<QueueRecyclable, Object>

View on GitHub (pinned to b2cfc85a57)