替换Redux状态切片属性值时JS堆内存持续增长问题求助
JS堆内存未释放问题原因分析
问题场景
使用CRA+Redux开发内存检测MVP,生产环境(npm run build + serve -s build)运行时内存表现异常:
- 点击「set data」按钮,JS堆内存增加100MB;
- 点击「clear」按钮后内存无下降;
- 三次点击「set data」后内存升至300MB,触发浏览器「Collect garbage」后内存仍保持300MB。
测试环境:Microsoft Edge 116.0.1938.69
相关代码
JavaScript代码
const { createRoot } = ReactDOM; const { Fragment, StrictMode, useEffect } = React; const { createStore } = Redux; const { Provider, useDispatch, useSelector } = ReactRedux; function reducer(state = { data: [] }, action) { switch (action.type) { case 'SET_DATA': return { ...state, data: new Array(1000000 * 100).join('a') }; case 'CLEAR': return { ...state, data: [] }; default: return state; } } const store = createStore(reducer); const App = () => { const dispatch = useDispatch(); const state = useSelector((state) => state.data); console.log({ state }); useEffect(() => { return () => { dispatch({ type: 'CLEAR' }); }; }, [dispatch]); return ( <div> <button onClick={() => dispatch({ type: 'SET_DATA' })}>set data</button> <button onClick={() => dispatch({ type: 'CLEAR' })}>clear</button> </div> ); }; const root = createRoot(document.getElementById("root")); root.render(<StrictMode><Provider store={store}><App /></Provider></StrictMode>);
HTML代码
<div id="root"></div> <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <script crossorigin src="https://unpkg.com/redux@4.2.1/dist/redux.js"></script> <script crossorigin src="https://unpkg.com/react-redux@8.1.2/dist/react-redux.js"></script>
核心原因
控制台日志持有内存引用
代码中console.log({ state })会在每次组件渲染时打印状态对象,浏览器控制台默认会持久化保存这些日志的引用。即使Redux状态已清空,包含大字符串的旧状态对象仍被控制台日志持有,无法被垃圾回收机制释放。Redux状态更新的残留引用
每次触发SET_DATA时,reducer会生成包含超大字符串的新状态对象。虽然CLEAR动作会将状态重置为空数组,但旧状态对象可能被ReactRedux的内部订阅缓存、或者未完成的渲染周期持有隐性引用,导致内存无法回收。浏览器内存缓存策略
浏览器为减少内存分配/回收的性能开销,会将已释放的内存保留为缓存池,即使手动触发垃圾回收,也不会立即将所有可回收内存归还给系统,表现为内存占用数值无明显下降。
内容的提问来源于stack exchange,提问作者Lin Du
相关产品推荐
相关产品推荐

