LeetCode中实例变量为何比局部变量占用更多内存?
我在LeetCode上用DFS解决图论问题时,发现用实例变量的代码比用局部变量的代码内存占用稳定高出约10%。我猜测可能是LeetCode复用了同一个Solution对象,但不清楚具体原因。
以下是两种实现代码:
局部变量版本
class Solution { public: void dfs(int node, int parent, int& counter, vector<int>& low, vector<int>& id_to_rank, vector<vector<int>>& edges, vector<vector<int>>& ans){ id_to_rank[node] = counter; low[node] = counter; counter++; for (const auto& e : edges[node]){ if (parent == e) continue; if (id_to_rank[e] == -1){ dfs(e, node, counter, low, id_to_rank, edges, ans); low[node] = min(low[node], low[e]); if (low[e] > id_to_rank[node]){ ans.push_back({e, node}); } }else{ low[node] = min(low[node], low[e]); } } return; } vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) { vector<vector<int>> edges(n); vector<int> id_to_rank (n, -1); vector<int> low(n); vector<vector<int>> ans; int counter = 0; for (const auto& c : connections){ edges[c[0]].push_back(c[1]); edges[c[1]].push_back(c[0]); } dfs(0, -1, counter, low, id_to_rank, edges, ans); return ans; } };
实例变量版本
class Solution { public: vector<vector<int>> ans; int counter = 0; void dfs(int node, int parent, vector<int>& low, vector<int>& id_to_rank, vector<vector<int>>& edges){ id_to_rank[node] = counter; low[node] = counter; counter++; for (const auto& e : edges[node]){ if (parent == e) continue; if (id_to_rank[e] == -1){ dfs(e, node, low, id_to_rank, edges); low[node] = min(low[node], low[e]); if (low[e] > id_to_rank[node]){ ans.push_back({e, node}); } }else{ low[node] = min(low[node], low[e]); } } return; } vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) { vector<vector<int>> edges(n); vector<int> id_to_rank (n, -1); vector<int> low(n); for (const auto& c : connections){ edges[c[0]].push_back(c[1]); edges[c[1]].push_back(c[0]); } dfs(0, -1, low, id_to_rank, edges); return ans; } };
你的猜测完全正确,LeetCode执行环境会复用同一个Solution类实例处理多组测试用例,这就是内存差异的核心原因:
实例变量的内存累积
局部变量版本中,ans和counter每次调用criticalConnections时都会重新初始化,函数执行结束后会随栈帧销毁或被内存回收。但实例变量版本中,ans和counter属于类实例本身,测试用例执行完毕后不会自动清空——下一次测试时,ans会保留上一次的结果继续添加元素,counter也会延续之前的数值。这直接导致实例变量占用的内存随着测试用例数量增加而持续累积,远大于单次测试所需的内存。内存统计的计算逻辑
LeetCode的内存统计会计算整个Solution实例的内存占用,包括所有实例变量。当ans累积了多组测试用例的结果后,它的内存占用会远高于单次测试时局部变量ans的大小。而局部变量仅在函数执行期间存在,执行结束后内存被回收,统计的是单次测试的峰值内存。额外的对象开销
实例变量属于对象的一部分,存储在堆上的对象内存块中,会附带对象头、内存对齐等细微的额外开销;而局部变量通常分配在栈上(或通过RAII管理的堆内存),这部分差异虽小,但也会加剧内存占用的差距。
验证方式
如果在实例变量版本的criticalConnections方法开头添加重置代码:
ans.clear(); counter = 0;
再提交测试,两种版本的内存占用差异会大幅缩小甚至消失,这就能直接验证是实例复用导致的内存累积问题。
内容的提问来源于stack exchange,提问作者frank880610

