release() consistently.Custom Struts tags are useful when repetitive JSP logic becomes messy, but poorly managed tag state can quietly consume memory until an application slows down, throws OutOfMemoryError, or requires constant restarts.
If you're still building your foundation, review core Struts tag concepts first. Related debugging material is also useful: troubleshooting custom tags,tag pooling issues,logging techniques,and packaging tag libraries.
Memory leaks in Java rarely mean memory is literally lost. Usually, objects remain reachable longer than intended. Garbage collection only removes unreachable objects, so one accidental reference can keep entire graphs alive.
Struts custom tags are especially vulnerable because:
public class UserTableTag extends TagSupport { private List users; public int doStartTag() { users = loadUsers(); return EVAL_BODY_INCLUDE; }}Looks harmless. It isn't.
If tag pooling is enabled and users is never cleared, the same tag instance can keep a reference to a large collection indefinitely.
A common misconception: “Request ends, so objects disappear.”
Not with pooled tags.
Containers may reuse the same tag instance many times. That means instance fields survive between requests unless explicitly reset.
| Without Pooling | With Pooling |
|---|---|
| Tag object discarded quickly | Tag reused repeatedly |
| Leak impact smaller | Leak accumulates over time |
| Bug harder to notice | Production memory climbs steadily |
Always reset fields after execution or inside release().
public class UserTableTag extends TagSupport { private List users; public int doEndTag() { users = null; return EVAL_PAGE; } public void release() { users = null; super.release(); }}This simple reset prevents large collections from staying attached to pooled tag instances.
These objects often reference huge object graphs.Keeping one session can accidentally preserve authentication state, uploaded files, user data, caches, and more.
Developers often check only the object they stored.They forget that one stored object may reference hundreds or thousands of others.
Example:
One forgotten field can hold megabytes.
private static Map cache = new HashMap();
This survives for application lifetime.Without eviction rules, memory only grows.
Some developers expect GC to “eventually handle it.”Garbage collection cannot remove reachable objects.
Saving helper objects that internally reference PageContext is effectively the same as storing PageContext itself.
Take heap dumps:
Compare retained objects and dominator trees.
If many objects are retained via custom tag classes, cleanup logic is incomplete.
Documentation, technical writing, and academic tasks often compete with engineering work. Some developers outsource non-core writing tasks when deadlines stack up.
Best for: fast academic support and student-focused workflows.
Strengths: simpler ordering flow, practical deadline handling, direct communication.
Weaknesses: smaller brand recognition.
Pricing: generally mid-range.
Useful feature: quick turnaround requests.
Best for: customizable writing orders with writer selection.
Strengths: flexible ordering, broad subject support.
Weaknesses: quality can vary depending on writer choice.
Pricing: moderate.
Useful feature: bidding system.
Best for: coaching-style writing help and editing support.
Strengths: revision support, structured assistance.
Weaknesses: fewer instant-order style options.
Pricing: varies by complexity.
Useful feature: guided academic assistance.
Yes. The issue is usually not the tag itself but retained references inside pooled instances. If a tag stores large lists, sessions, or helper objects and fails to clear them, those objects remain reachable. Over time, traffic amplifies the problem until heap pressure becomes severe. Production systems with moderate traffic can hide this issue for weeks before it becomes obvious.
Not always. release() is important, but relying only on it is risky. Some state should be reset earlier in doEndTag() or after body processing. Defensive cleanup reduces retained references even if lifecycle behavior changes or execution exits unexpectedly.
Disabling pooling can reduce some risks but is usually the wrong first move. Pooling exists for performance reasons. A properly written custom tag should work safely with pooling enabled. Fix lifecycle handling instead of using configuration as a bandage.
At minimum, profile after introducing new tags, changing tag attributes, or modifying caching behavior. Memory issues are easier to fix when caught early than after production traffic magnifies them.
Absolutely. Complexity is not required for leaks. Even a tiny tag with one forgotten field can retain a surprisingly large object graph if that field references request data, page state, or collections.
Yes. Logging frameworks themselves are usually safe, but custom wrappers, appenders, or context objects stored carelessly can retain references. Logging-related helper classes should be reviewed like any other object.