应明确对象生命周期、控制数据加载粒度、善用引用类型、精简对象结构。具体包括:避免静态集合长引用;用WeakHashMap或SoftReference管理缓存;流式处理大数据;用基本类型替代包装类;扁平化对象结构。

明确对象生命周期,避免长引用滞留
很多内存问题源于对象本该被回收却因意外持有而“活”得太久。比如静态集合缓存了大量业务对象,或监听器注册后未反注册,导致整个对象图无法释放。关键不是“有没有引用”,而是“引用是否还有业务意义”。建议:非必要不使用 static 集合;用 WeakHashMap 存储回调或监听关系;对临时大对象(如解析后的 JSON 树、Excel 行数据)确保作用域结束即脱离强引用链。
控制数据加载粒度,拒绝“全量进内存”惯性
数据库查 100 万条记录塞进 ArrayList,或一次读取几百 MB 文件转成字符串——这类操作是 OOM 的高频诱因。应改用流式处理:JDBC 用 setFetchSize + 游标分页;文件解析用 SAX 或 Jackson Streaming API;集合操作优先选 Stream.iterate 或自定义 Spliterator。核心原则:内存中只保留当前计算所需的数据子集,其余交给磁盘或数据库缓冲。
善用引用类型,让缓存更“懂进退”
普通 HashMap 缓存容易吃光堆内存。可依场景选择:
• 短期热点数据 → SoftReference:JVM 内存紧张时自动回收;
• 仅需“弱关联”的映射(如 Class → 元数据)→ WeakReference 或 WeakHashMap;
• 明确时效的缓存 → 配合 Caffeine 等库,设 maximumSize 和 expireAfterWrite。
注意:软/弱引用对象被回收后,get() 返回 null,代码需做空值防护,不可假设一定存在。
精简对象结构,减少隐式内存开销
一个看似简单的 POJO,可能因字段类型、继承链、包装类等带来数倍内存膨胀。例如:
• 用 int 代替 Integer(避免装箱和对象头);
• 字符串频繁截取时,用 String.substring()(Java 7u6 后已不共享 char[],但仍有优化空间)或直接操作 char[];
• 避免深度嵌套对象,考虑扁平化结构或用 byte[] + 序列化协议(如 Protobuf)压缩存储;
• 日志中打印对象时,重写 toString() 限制输出长度,防止日志框架递归 toString 拖垮内存。


















