Skip to content

Commit

Permalink
[Draft] Add feature flag: enableStrongMemoryCleanup
Browse files Browse the repository at this point in the history
Add a feature flag that will test doing a recursive
clean of an unmount node. This will disconnect the
fiber graph making leaks less severe.
  • Loading branch information
bgirard committed Mar 20, 2021
1 parent 00d4f95 commit 6cf1cf4
Show file tree
Hide file tree
Showing 20 changed files with 83 additions and 2 deletions.
4 changes: 4 additions & 0 deletions packages/react-art/src/ReactARTHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,7 @@ export function afterActiveInstanceBlur() {
export function preparePortalMount(portalInstance: any): void {
// noop
}

export function unmountNode(node: any): void {
// noop
}
11 changes: 11 additions & 0 deletions packages/react-dom/src/client/ReactDOMComponentTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
const internalEventHandlesSetKey = '__reactHandles$' + randomKey;

export function unmountNode(
node: Instance | TextInstance | SuspenseInstance | ReactScopeInstance,
): void {
delete (node: any)[internalInstanceKey];
delete (node: any)[internalPropsKey];
delete (node: any)[internalContainerInstanceKey];
delete (node: any)[internalEventHandlersKey];
delete (node: any)[internalEventHandlerListenersKey];
delete (node: any)[internalEventHandlesSetKey];
}

export function precacheFiberNode(
hostInst: Fiber,
node: Instance | TextInstance | SuspenseInstance | ReactScopeInstance,
Expand Down
5 changes: 5 additions & 0 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getFiberFromScopeInstance,
getInstanceFromNode as getInstanceFromNodeDOMTree,
isContainerMarkedAsRoot,
unmountNode as unmountNodeFromDOMTree,
} from './ReactDOMComponentTree';
import {hasRole} from './DOMAccessibilityRoles';
import {
Expand Down Expand Up @@ -1209,3 +1210,7 @@ export function setupIntersectionObserver(
},
};
}

export function unmountNode(node: any): void {
unmountNodeFromDOMTree(node);
}
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,7 @@ export function afterActiveInstanceBlur() {
export function preparePortalMount(portalInstance: Instance): void {
// noop
}

export function unmountNode(node: any): void {
// noop
}
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactNativeHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,7 @@ export function afterActiveInstanceBlur() {
export function preparePortalMount(portalInstance: Instance): void {
// noop
}

export function unmountNode(node: any): void {
// noop
}
2 changes: 2 additions & 0 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
getInstanceFromScope() {
throw new Error('Not yet implemented.');
},

unmountNode() {},
};

const hostConfig = useMutation
Expand Down
19 changes: 18 additions & 1 deletion packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
enableSuspenseCallback,
enableScopeAPI,
enableStrictEffects,
enableStrongMemoryCleanup,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
Expand All @@ -60,6 +61,7 @@ import {
hasCaughtError,
clearCaughtError,
} from 'shared/ReactErrorUtils';
import {unmountNode} from './ReactFiberHostConfig';
import {
NoFlags,
ContentReset,
Expand Down Expand Up @@ -1212,9 +1214,24 @@ function detachFiberMutation(fiber: Fiber) {
fiber.return = null;
}

export function detachFiberAfterEffects(fiber: Fiber): void {
export function detachFiberAfterEffects(
fiber: Fiber,
recurseIntoSibbling: ?boolean,
): void {
// Null out fields to improve GC for references that may be lingering (e.g. DevTools).
// Note that we already cleared the return pointer in detachFiberMutation().
if (enableStrongMemoryCleanup) {
if (fiber.child) {
detachFiberAfterEffects(fiber.child, true);
}
if (fiber.sibling && recurseIntoSibbling === true) {
detachFiberAfterEffects(fiber.sibling, true);
}
if (fiber.stateNode) {
unmountNode(fiber.stateNode);
}
fiber.return = null;
}
fiber.alternate = null;
fiber.child = null;
fiber.deletions = null;
Expand Down
19 changes: 18 additions & 1 deletion packages/react-reconciler/src/ReactFiberCommitWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
enableSuspenseCallback,
enableScopeAPI,
enableStrictEffects,
enableStrongMemoryCleanup,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
Expand All @@ -60,6 +61,7 @@ import {
hasCaughtError,
clearCaughtError,
} from 'shared/ReactErrorUtils';
import {unmountNode} from './ReactFiberHostConfig';
import {
NoFlags,
ContentReset,
Expand Down Expand Up @@ -1212,9 +1214,24 @@ function detachFiberMutation(fiber: Fiber) {
fiber.return = null;
}

export function detachFiberAfterEffects(fiber: Fiber): void {
export function detachFiberAfterEffects(
fiber: Fiber,
recurseIntoSibbling: ?boolean,
): void {
// Null out fields to improve GC for references that may be lingering (e.g. DevTools).
// Note that we already cleared the return pointer in detachFiberMutation().
if (enableStrongMemoryCleanup) {
if (fiber.child) {
detachFiberAfterEffects(fiber.child, true);
}
if (fiber.sibling && recurseIntoSibbling === true) {
detachFiberAfterEffects(fiber.sibling, true);
}
if (fiber.stateNode) {
unmountNode(fiber.stateNode);
}
fiber.return = null;
}
fiber.alternate = null;
fiber.child = null;
fiber.deletions = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const preparePortalMount = $$$hostConfig.preparePortalMount;
export const prepareScopeUpdate = $$$hostConfig.preparePortalMount;
export const getInstanceFromScope = $$$hostConfig.getInstanceFromScope;
export const getCurrentEventPriority = $$$hostConfig.getCurrentEventPriority;
export const unmountNode = $$$hostConfig.unmountNode;

// -------------------
// Microtasks
Expand Down
4 changes: 4 additions & 0 deletions packages/react-test-renderer/src/ReactTestHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,7 @@ export function prepareScopeUpdate(scopeInstance: Object, inst: Object): void {
export function getInstanceFromScope(scopeInstance: Object): null | Object {
return nodeToInstanceMap.get(scopeInstance) || null;
}

export function unmountNode(node: any): void {
// noop
}
3 changes: 3 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ export const disableNativeComponentFrames = false;
// If there are no still-mounted boundaries, the errors should be rethrown.
export const skipUnmountedBoundaries = false;

// When a node is unmounted, recurse into the Fiber subtree and clean out references.
export const enableStrongMemoryCleanup = false;

// --------------------------
// Future APIs to be deprecated
// --------------------------
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.testing.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.testing.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const enableLegacyFBSupport = !__EXPERIMENTAL__;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = true;
export const enableStrongMemoryCleanup = false;

export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const disableNativeComponentFrames = false;
export const createRootStrictEffectsByDefault = false;
export const enableStrictEffects = false;
export const enableUseRefAccessWarning = __VARIANT__;
export const enableStrongMemoryCleanup = __VARIANT__;

export const enableProfilerNestedUpdateScheduledHook = __VARIANT__;
export const disableSchedulerTimeoutInWorkLoop = __VARIANT__;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const {
disableSchedulerTimeoutInWorkLoop,
enableSyncMicroTasks,
enableLazyContextPropagation,
enableStrongMemoryCleanup,
} = dynamicFeatureFlags;

// On WWW, __EXPERIMENTAL__ is used for a new modern build.
Expand Down

0 comments on commit 6cf1cf4

Please sign in to comment.