Java不手动free是因为对象生命周期由GC Roots可达性决定而非程序员控制;静态集合未清理、监听器未反注册、ThreadLocal未remove等会导致内存泄漏;分代回收匹配对象寿命分布,JVM参数需按生产环境调优。

Java不手动free,是因为对象生死不由程序员定
Java里你写 new Person(),JVM立刻在堆上分配内存;但没人告诉你“该在哪调用free”——因为根本就没有这个函数。这不是偷懒,而是设计使然:对象是否还“活着”,取决于它能否从GC Roots(比如栈帧里的局部变量、静态字段、活动线程)出发被引用链到达。只要还有强引用连着,哪怕你主观上“用完了”,JVM也必须保留它;一旦断链,哪怕你主观上还想用,它也可能下一秒就被回收。这种“可达性语义”取代了C/C++里“谁分配谁释放”的所有权模型。
自动管理不是全自动,内存泄漏照样发生
常见错误现象:OutOfMemoryError: Java heap space 不一定代表堆太小,更可能是本该释放的对象被意外持有了。比如:
- 静态集合类(
static Map)不断put却忘了remove或没设过期策略cache - 监听器注册后未反注册(GUI或事件总线场景),导致持有Activity/Fragment/Handler等大对象引用
- ThreadLocal变量未调用
remove(),在线程池复用场景下造成隐式内存泄漏
这些都不是GC机制失效,而是你写的代码悄悄延长了对象的“可达生命周期”。GC只管“不可达”,不管“不该达”。
分代回收不是为了炫技,是为匹配真实对象寿命分布
你创建的大多数对象(比如方法内临时字符串、DTO、循环中的计数器)存活时间极短;少数对象(如Spring单例Bean、数据库连接池)则贯穿整个应用生命周期。JVM据此把堆拆成新生代(Eden + Survivor)和老年代,分别用不同算法处理:
立即学习“Java免费学习笔记(深入)”;
本文档主要讲述的是关于Objective-C手动内存管理的规则;在ios开发中Objective-C 增加了一些新的东西,包括属性和垃圾回收。那么,我们在学习Objective-C之前,最好应该先了解,从前是什么样的,为什么Objective-C 要增加这些支持。有需要的朋友可以下载看看
- 新生代用复制算法(
Minor GC):快、停顿短,适合高频创建销毁 - 老年代用标记-整理(
Major GC)或G1混合回收:避免碎片,适合长期存活对象 - 如果一个大对象(比如
new byte[1024 * 1024 * 100])直接进老年代,就绕过新生代拷贝,减少开销
这说明:自动管理背后有明确的性能权衡,不是无脑扫全堆。
JVM参数不是摆设,不调就等于放弃控制权
默认堆大小(如-Xms/-Xmx)往往不适合生产环境。容易踩的坑:
- 不设
-Xmn,新生代过小 → 频繁Minor GC,CPU花在GC上而不是业务上 - 老年代占比过高(
-XX:NewRatio=8)→ 小对象也容易晋升失败,触发Full GC - 忽略元空间(
-XX:MetaspaceSize)→ 动态加载类多(如Spring Boot+热部署)时,java.lang.OutOfMemoryError: Metaspace悄然而至
真正关键的不是记全参数,而是学会看GC日志:-Xlog:gc*:file=gc.log:time,tags,level —— 日志里每行GC(3) Pause Young或GC(7) Pause Full都在告诉你,当前内存模型正如何响应你的代码行为。
最常被忽略的一点:自动内存管理解放的是“释放时机”的决策权,但“何时创建”“持有多久”“引用谁”这三个动作,始终牢牢攥在你手里。









