局部变量应存放一次性中间结果,成员变量仅保留对象真正拥有的状态;错误地将临时数据设为成员变量会增加堆内存压力、拖慢gc。

因为局部变量存在栈上、方法结束就自动消失,而成员变量钉在堆里、要等对象被GC回收才释放。把本该临时用的数据塞进成员变量,等于人为制造大量“僵尸数据”,直接拖慢垃圾回收、推高内存压力。
局部变量适合放“一次性的中间结果”
比如循环计数器、格式化后的字符串、临时集合、解析上下文等——这些值只在当前方法内有效,用完即弃。声明为局部变量后,JVM在方法返回时直接弹出整个栈帧,不触发GC,也不占堆空间。
- 错误做法:把
private List<string> tempResults;</string>声明为成员变量,每次处理都clear()复用 - 正确做法:在方法内
List<string> results = new ArrayList();</string>,用完自然消失 - Lambda中捕获的变量也属局部语义,若误提升为成员变量,会导致外部对象无法及时回收(尤其持有大缓存或IO资源时)
成员变量只保留“对象真正拥有的状态”
它代表的是这个实例“是谁”“有什么属性”,比如 User.name、Order.totalPrice。一旦把临时工具型数据(如 buffer、parserContext)塞进来,每个对象实例都会多扛一份长期存活的堆内存。
- 1000个
Processor实例,若含一个private byte[] workBuffer = new byte[8192];,就额外占用约8MB堆内存,且长期存活,容易晋升到老年代 - 静态成员变量更危险:它属于类本身,整个JVM生命周期存在;仅适合配置常量、单例引用等真正全局不变的数据
- 判断标准很简单:这个值是不是“离开这个对象就失去意义”?如果不是,就不该是成员变量
栈快但有限,堆大但要GC——别错配
栈访问极快、无同步开销、天然线程安全,但默认大小仅1MB左右(-Xss 可调)。不能因为“栈快”就滥用:
- 避免在递归或深层嵌套中反复创建大数组、长字符串——栈帧撑爆会抛
StackOverflowError - 局部变量可以持对象引用(如
String s = "hello";),但对象本体仍在堆,栈只存指针(4或8字节) - 高频调用的方法里,重复
new HashMap()会造成GC压力;可考虑复用局部引用,或改用堆上对象池(需权衡GC成本)
本质不是写法偏好,而是让数据生命周期和内存区域特性对齐:短命数据走栈,长命状态留堆。对齐了,GC就轻松,延迟就下降,内存就实在。











