{"record":{"id":"cb20deb52face8ac","repo":"stanfordnlp/CoreNLP","slug":"cached-annotator-will-never-gc-this-can-cause-o","errorCode":null,"errorMessage":"Cached annotator will never GC -- this can cause OOM exceptions!","messagePattern":"Cached annotator will never GC -- this can cause OOM exceptions!","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/edu/stanford/nlp/pipeline/AnnotatorPool.java","lineNumber":42,"sourceCode":"\n  /** A logger for this class */\n  private static final Redwood.RedwoodChannels log = Redwood.channels(AnnotatorPool.class);\n\n  /**\n   * A cached annotator, including the signature it should cache on.\n   */\n  private static class CachedAnnotator {\n    /** The signature of the annotator. */\n    public final String signature;\n    /** The cached annotator. */\n    public final Lazy<Annotator> annotator;\n\n    /**\n     * The straightforward constructor.\n     */\n    private CachedAnnotator(String signature, Lazy<Annotator> annotator) {\n      if (!annotator.isCache()) {\n        log.warn(\"Cached annotator will never GC -- this can cause OOM exceptions!\");\n      }\n      this.signature = signature;\n      this.annotator = annotator;\n    }\n\n    /** {@inheritDoc} */\n    @Override\n    public boolean equals(Object o) {\n      if (this == o) return true;\n      if (o == null || getClass() != o.getClass()) return false;\n      CachedAnnotator that = (CachedAnnotator) o;\n      return Objects.equals(signature, that.signature) && (Objects.equals(annotator, that.annotator));\n    }\n\n    /** {@inheritDoc} */\n    @Override\n    public int hashCode() {\n      int result = signature != null ? signature.hashCode() : 0;","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/pipeline/AnnotatorPool.java#L24-L60","documentation":"AnnotatorPool's CachedAnnotator constructor warns when the wrapped Lazy<Annotator> is not a caching Lazy. A non-caching Lazy holds only a supplier, so every annotation request may rebuild the annotator, and the constructed annotator instance is strongly held without the cache mechanism that allows garbage collection — leading to unbounded memory growth and potential OutOfMemoryError.","triggerScenarios":"Constructing a CachedAnnotator with Lazy.of(...) (non-caching) instead of Lazy.cache(...), typically when registering a custom annotator into an AnnotatorPool.","commonSituations":"Custom pipeline code that plugs annotators into Stanford CoreNLP's AnnotatorPool with a hand-rolled Lazy wrapper; long-running servers that annotate many documents and hold the pool for the process lifetime.","solutions":["Wrap the annotator supplier with Lazy.cache(...) (or use Lazy.of followed by .cache()) so the pool's caching/GC behavior applies.","If the annotator is stateless and cheap, keep the non-caching lazy but be aware it will be rebuilt per request; better to cache it anyway.","In long-running services, avoid re-creating AnnotatorPools; reuse a single cached pool and monitor heap usage."],"exampleFix":"// before\nAnnotatorPool.AnnotationFactory factory = (signature, props) ->\n    new AnnotatorPool.CachedAnnotator(signature, Lazy.of(() -> new MyAnnotator(props)));\n\n// after\nAnnotatorPool.AnnotationFactory factory = (signature, props) ->\n    new AnnotatorPool.CachedAnnotator(signature, Lazy.cache(() -> new MyAnnotator(props)));","handlingStrategy":"validation","validationCode":"// Only construct CachedAnnotator with a caching Lazy\nLazy<Annotator> lazy = Lazy.cache(() -> new MyAnnotator(props));\nif (!lazy.isCache()) {\n  throw new IllegalArgumentException(\"Annotator lazy must be caching to avoid GC leak\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use Lazy.cache(...) when registering annotators in an AnnotatorPool","Watch heap usage in long-running annotation services; growth between pools signals this leak","Reuse a single AnnotatorPool across requests instead of rebuilding it","Run load tests with many documents to surface OOM before production"],"tags":["nlp","memory-leak","resource-management"],"backgroundTag":"memory-leak-warning","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}