Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak when using atomFamily/selectorFamily #366

Open
alanhoff opened this issue Jun 19, 2020 · 22 comments
Open

Memory leak when using atomFamily/selectorFamily #366

alanhoff opened this issue Jun 19, 2020 · 22 comments
Assignees
Labels
help wanted Extra attention is needed performance

Comments

@alanhoff
Copy link

alanhoff commented Jun 19, 2020

Hello there,

The resources created by atomFamily and selectorFamily are not being properly freed when the specific key changes or if the component using the related RecoilState<T> unmounts.

Since I believe that memory management isn't properly implemented yet, both utils should be marked as _UNSAFE.

https://codesandbox.io/s/cranky-booth-penix?file=/src/App.js

Screen Shot 2020-06-19 at 12 07 12

@trivigy
Copy link

trivigy commented Nov 9, 2020

Could this be related? I am getting this warning and cannot seem to figure out a way to get rid of it.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

@ltwlf
Copy link

ltwlf commented Jan 3, 2021

I have the same issue like @trivigy in a unit test with react testing library. Can't get rid of the warning.

@drarmstr
Copy link
Contributor

drarmstr commented Jan 4, 2021

I don't think that warning should be related.

@ianstormtaylor
Copy link

I'm running into the same error as @trivigy and @ltwlf and can't figure out what's causing it.

@mondaychen
Copy link
Contributor

@trivigy did you unmount RecoilRoot or just component using the selector? If the whole root then it might related to what I found in #897

@trivigy
Copy link

trivigy commented Feb 23, 2021

@mondaychen Still experience the same issue but I just squint my eye to not see so it wouldn't bother me. 😆

@chybisov
Copy link

chybisov commented Mar 31, 2021

Could this be related? I am getting this warning and cannot seem to figure out a way to get rid of it.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

I've started to see errors like that more often with version 0.2.0. Though I don't use async selectors.

@mondaychen
Copy link
Contributor

@trivigy @ltwlf @chybisov @ianstormtaylor I don't think the warning is related to the memory issue related to family utils. Let's start a new thread to discuss that. It will be really helpful if anyone can provide a way to reproduce the issue you are describing and create a new issue with that.

@ianstormtaylor
Copy link

@mondaychen I think you're right. I added a comment here: #951 (comment)

@mondaychen
Copy link
Contributor

Just FYI we are working on the memory issue with use of family utils, and should have something to announce about it in the near future.

@KevinMusgrave
Copy link

Any update on this?

@ofirzBM
Copy link

ofirzBM commented Jan 5, 2022

Hi All,

Can we use 'most-recent' instead of 'keep-all' here in atom Family?
https://github.com/facebookexperimental/Recoil/blob/main/packages/recoil/recoil_values/Recoil_atomFamily.js#L90

@drarmstr
Copy link
Contributor

drarmstr commented Jan 6, 2022

Hi All,

Can we use 'most-recent' instead of 'keep-all' here in atom Family? https://github.com/facebookexperimental/Recoil/blob/main/packages/recoil/recoil_values/Recoil_atomFamily.js#L90

That would mean only the most recent atom used in the family would be retained.

@mejackreed
Copy link

Hi @mondaychen . Does the recent release (0.7.4) address any of this, or are those leaks elsewhere?

@luishdz1010
Copy link
Contributor

My current solution is to create all atom families in a React Context and useMemo them. Then components can do:

const { myAtomFamily } = useAppAtoms()
const value = useRecoilValue(myAtomFamily(key))

This way the created atoms are scoped to the lifetime of the Context.Provider and I don't need to fiddle the the cache strategy.

This approach does releases the atom objects themselves when the app unmounts
(or a server render finishes), but they are still referenced by https://github.com/facebookexperimental/Recoil/blob/main/packages/recoil/core/Recoil_Node.js#L92, and also triggers warnings for duplicated Atoms after the second render.

Is there any doc outlining how memory management works in recoil? Is this strategy flawed?

@drarmstr
Copy link
Contributor

That solution only controls the lifetime of the atom objects or the atom family closure which contains the dictionary of cached atom objects. While removing that would remove some minimal memory, the actual registered atom accounting structures, selectors, and selector caches would still be held in the runtime. If you would like to explore or finish memory management in Recoil use the recoil_memory_managament_2020 feature flag in Recoil_gkx.js

@Tomas2D
Copy link

Tomas2D commented Oct 12, 2022

@drarmstr can you be more specific on how we can achieve it?

@drarmstr
Copy link
Contributor

You can add recoil_memory_management_2020 to gks in Recoil_gkx.js and then explore with retainedBy_UNSTABLE atom option, useRetain(), etc.

Would be interesting if someone wanted to do a PR for mapping GK flags here with the new RecoilEnv.js mechanism to more easily dynamically adjust features without rebuilding..

@drarmstr drarmstr added the help wanted Extra attention is needed label Oct 12, 2022
facebook-github-bot pushed a commit that referenced this issue Nov 3, 2022
Summary:
In #366 (comment), it was suggested that we could possibly use `RecoilEnv` to dynamically enable GKs:

> Would be interesting if someone wanted to do a PR for mapping GK flags here with the new `RecoilEnv.js` mechanism to more easily dynamically adjust features without rebuilding..

Well, I wanted to try out the transition support in the same way OP of that issue wanted memory management, so I decided to give the implementation a try: this change adds a new property to `RecoilEnv`, `RECOIL_GKS_ENABLED_UNSTABLE`, which can be used to enable certain GKs at runtime. When the property is updated, its changes are proxied to the underlying GK implementation.

Pull Request resolved: #2078

Test Plan: CI, unit tests

Reviewed By: drarmstr

Differential Revision: D40688490

Pulled By: wd-fb

fbshipit-source-id: 8a748d453e22782c6122a8d5047155019c5d3a1c
@AkifumiSato
Copy link
Contributor

The RECOIL_GKS_ENABLED has already been taken in and the recoil_memory_management_2020 appears to be set by default.
What else should be done to eliminate memory leaks?

@taylorcode
Copy link

taylorcode commented Feb 10, 2023

retainedBy_UNSTABLE does not appear to work, at least not in version 0.7.6. #2173

The linked PR (#2143) does not fix this issue

recoil_memory_management_2020 is enabled by default in this version.

If the retainedBy: 'components' option worked it would be amazing...Automatic cleanup of atoms when no components depend on them directly or transitively through other selectors, really great idea.

@Tomas2D
Copy link

Tomas2D commented Feb 11, 2023

Maybe guys you could try something I did #1864 (comment)

snipershooter0701 pushed a commit to snipershooter0701/Recoil that referenced this issue Mar 5, 2023
Summary:
In facebookexperimental/Recoil#366 (comment), it was suggested that we could possibly use `RecoilEnv` to dynamically enable GKs:

> Would be interesting if someone wanted to do a PR for mapping GK flags here with the new `RecoilEnv.js` mechanism to more easily dynamically adjust features without rebuilding..

Well, I wanted to try out the transition support in the same way OP of that issue wanted memory management, so I decided to give the implementation a try: this change adds a new property to `RecoilEnv`, `RECOIL_GKS_ENABLED_UNSTABLE`, which can be used to enable certain GKs at runtime. When the property is updated, its changes are proxied to the underlying GK implementation.

Pull Request resolved: facebookexperimental/Recoil#2078

Test Plan: CI, unit tests

Reviewed By: drarmstr

Differential Revision: D40688490

Pulled By: wd-fb

fbshipit-source-id: 8a748d453e22782c6122a8d5047155019c5d3a1c
@tien
Copy link

tien commented Mar 26, 2023

Building on @Tomas2D finding, this is my solution for now until retainedBy is fully functional.
You'll need to patch recoil to exports releaseNode & useStoreRef

import { useCallback, useEffect } from 'react'
import { releaseNode, useGetRecoilValueInfo_UNSTABLE, useRecoilCallback, useStoreRef } from 'recoil'

type RECOIL_GARBAGE_COLLECTOR_UNSTABLE_Props = {
  shouldCheckForGarbageCollection: (key: string) => boolean
  interval: number
}

/**
 * TODO: remove after `retainedBy` support is implemented on recoil
 */
export const RECOIL_GARBAGE_COLLECTOR_UNSTABLE = (props: RECOIL_GARBAGE_COLLECTOR_UNSTABLE_Props) => {
  const storeRef = useStoreRef()
  const getRecoilValueInfo = useGetRecoilValueInfo_UNSTABLE()

  const releaseUnusedNodes = useRecoilCallback(
    ({ snapshot }) =>
      () => {
        if (storeRef.current === null) {
          return
        }

        const releaseSnapshot = snapshot.retain()
        const state = storeRef.current.getState()

        for (const node of snapshot.getNodes_UNSTABLE()) {
          if (!state.knownAtoms.has(node.key) && !state.knownSelectors.has(node.key)) {
            continue
          }

          if (props.shouldCheckForGarbageCollection(node.key)) {
            const firstNodeSubscriber = snapshot.getInfo_UNSTABLE(node).subscribers.nodes[Symbol.iterator]().next()

            // Can't use `snapshot.getInfo_UNSTABLE` here
            // https://github.com/facebookexperimental/Recoil/issues/1684
            const firstComponentSubscriber = getRecoilValueInfo(node).subscribers.components[Symbol.iterator]().next()

            const hasNoSubscriber =
              firstNodeSubscriber.done &&
              firstNodeSubscriber.value === undefined &&
              firstComponentSubscriber.done &&
              firstComponentSubscriber.value === undefined

            if (hasNoSubscriber) {
              releaseNode(storeRef.current, state.currentTree, node.key)
            }
          }
        }

        releaseSnapshot()
      },
    [getRecoilValueInfo, props, storeRef]
  )

  useEffect(() => {
    const interval = setInterval(releaseUnusedNodes, props.interval)

    return () => clearInterval(interval)
  }, [props.interval, releaseUnusedNodes])

  return null
}

Which can be used like so

<RECOIL_GARBAGE_COLLECTOR_UNSTABLE
  interval={5_000}
  shouldCheckForGarbageCollection={useCallback(
    node => node.startsWith('MyState') || node.startsWith('SomeOtherState'),
    []
  )}
/>

This will basically check for & release all unused nodes every [x configurable interval].

eagle2722 added a commit to eagle2722/Recoil that referenced this issue Sep 21, 2024
Summary:
In facebookexperimental/Recoil#366 (comment), it was suggested that we could possibly use `RecoilEnv` to dynamically enable GKs:

> Would be interesting if someone wanted to do a PR for mapping GK flags here with the new `RecoilEnv.js` mechanism to more easily dynamically adjust features without rebuilding..

Well, I wanted to try out the transition support in the same way OP of that issue wanted memory management, so I decided to give the implementation a try: this change adds a new property to `RecoilEnv`, `RECOIL_GKS_ENABLED_UNSTABLE`, which can be used to enable certain GKs at runtime. When the property is updated, its changes are proxied to the underlying GK implementation.

Pull Request resolved: facebookexperimental/Recoil#2078

Test Plan: CI, unit tests

Reviewed By: drarmstr

Differential Revision: D40688490

Pulled By: wd-fb

fbshipit-source-id: 8a748d453e22782c6122a8d5047155019c5d3a1c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed performance
Projects
None yet
Development

No branches or pull requests