Skip to main content
Glama

get-memory-optimization

Debug memory leaks and optimize performance in React Native Expo apps by identifying common issues like useEffect cleanup, event listeners, timers, and closure leaks.

Instructions

Get memory optimization patterns. Call this when debugging memory leaks or performance issues. Covers useEffect cleanup (listeners, timers), closure memory leaks, React Native DevTools memory profiler, view flattening, R8 shrinking for Android, and a common memory leak sources checklist. Use topic to get a specific section only.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
topicNoGet a specific section only. Available: useeffect-cleanup, event-listeners, timers, closures, devtools-profiler, frame-budget, view-flattening, r8, checklist. Omit for full content.
compactNoIf true, returns rules only without code examples. Much shorter.

Implementation Reference

  • The `getMemoryOptimization` function, which acts as the handler for the `get-memory-optimization` tool, calling `resolvePattern` with the predefined pattern content.
    export const getMemoryOptimization = (topic?: string, compact?: boolean): string =>
      resolvePattern(pattern, topic, compact);
  • src/index.ts:238-256 (registration)
    Registration of the `get-memory-optimization` MCP tool in `src/index.ts`.
    server.tool(
      "get-memory-optimization",
      "Get memory optimization patterns. Call this when debugging memory leaks or performance issues. Covers useEffect cleanup (listeners, timers), closure memory leaks, React Native DevTools memory profiler, view flattening, R8 shrinking for Android, and a common memory leak sources checklist. Use `topic` to get a specific section only.",
      {
        topic: z
          .string()
          .optional()
          .describe(
            "Get a specific section only. Available: useeffect-cleanup, event-listeners, timers, closures, devtools-profiler, frame-budget, view-flattening, r8, checklist. Omit for full content."
          ),
        compact: z
          .boolean()
          .optional()
          .describe("If true, returns rules only without code examples. Much shorter."),
      },
      async ({ topic, compact }) => ({
        content: [{ type: "text", text: getMemoryOptimization(topic, compact) }],
      })
    );
  • Definition of the sections and content for the memory optimization tool.
    const sections: Record<string, string> = {
      'useeffect-cleanup': `## useEffect Cleanup — Always Return a Cleanup Function
    
    Every \`useEffect\` that creates a side effect must return a cleanup function:
    
    \`\`\`tsx
    // GOOD — cleanup prevents memory leaks
    useEffect(() => {
      const subscription = eventEmitter.addListener('event', handler);
      return () => subscription.remove();
    }, []);
    
    // BAD — listener leaks after component unmounts
    useEffect(() => {
      eventEmitter.addListener('event', handler); // never cleaned up
    }, []);
    \`\`\``,
    
      'event-listeners': `## Event Listener Cleanup
    
    \`\`\`tsx
    import { AppState, AppStateStatus, Keyboard } from 'react-native';
    
    const MyComponent = () => {
      useEffect(() => {
        const appStateSub = AppState.addEventListener('change', (state: AppStateStatus) => {
          if (state === 'active') syncData();
        });
    
        const keyboardShowSub = Keyboard.addListener('keyboardDidShow', handleKeyboardShow);
        const keyboardHideSub = Keyboard.addListener('keyboardDidHide', handleKeyboardHide);
    
        return () => {
          appStateSub.remove();
          keyboardShowSub.remove();
          keyboardHideSub.remove();
        };
      }, []);
    };
    \`\`\``,
    
      timers: `## Timer Cleanup
    
    \`\`\`tsx
    const PollingComponent = () => {
      useEffect(() => {
        const interval = setInterval(() => {
          fetchLatestData();
        }, 5000);
    
        return () => clearInterval(interval); // stop polling on unmount
      }, []);
    };
    
    const DelayedAction = () => {
      useEffect(() => {
        const timeout = setTimeout(() => {
          performAction();
        }, 2000);
    
        return () => clearTimeout(timeout); // cancel if component unmounts first
      }, []);
    };
    \`\`\``,
    
      closures: `## Closure Memory Leaks
    
    Closures capture references. Capture only the specific values you need:
    
    \`\`\`tsx
    // BAD — captures entire user object; keeps stale reference alive
    useEffect(() => {
      analytics.identify(user); // holds reference to entire user
    }, [user]);
    
    // GOOD — capture only the specific value needed
    const userId = user.id;
    useEffect(() => {
      analytics.identify(userId);
    }, [userId]);
    \`\`\``,
    
      'devtools-profiler': `## React Native DevTools Memory Profiler
    
    Use React Native DevTools to identify memory leaks:
    
    1. Open DevTools via Expo dev menu → "Open JS Debugger"
    2. Go to the **Memory** tab
    3. Take a **heap snapshot** — this is your baseline
    4. Reproduce the suspected leak (navigate to screen, perform actions, navigate away)
    5. Take another heap snapshot
    6. Compare: look for objects that should have been garbage collected
    
    **Key indicators:**
    - **Blue bars** — currently allocated memory
    - **Grey bars** — freed (garbage collected) memory
    - **Shallow size** — memory held by the object itself
    - **Retained size** — memory that would be freed if this object were collected
    
    **Red flag:** Objects from a screen still present in memory after navigating away.`,
    
      'frame-budget': `## 16ms Frame Budget (60 FPS)
    
    Each frame must render in 16ms for 60 FPS (8ms for 120 FPS ProMotion). Exceeding this causes dropped frames.
    
    - **JS thread**: business logic, state updates, React reconciliation
    - **UI thread**: layout, painting, touch handling
    - **Reanimated worklets**: run on UI thread, never block JS
    
    Keep heavy synchronous work off both threads.
    
    > Defer heavy work after animations with InteractionManager — see \`get-performance-patterns\` (topic: interaction-manager).`,
    
      'view-flattening': `## View Flattening
    
    React Native automatically flattens "layout-only" nodes in the view hierarchy (New Architecture). This reduces depth and improves render performance.
    
    When a child view gets unexpectedly flattened inside a native component expecting a specific number of children, use \`collapsable={false}\` to prevent flattening:
    
    \`\`\`tsx
    <MyNativeComponent>
      <Child1 collapsable={false} />
      <Child2 collapsable={false} />
      <Child3 collapsable={false} />
    </MyNativeComponent>
    \`\`\`
    
    **Debug view hierarchy:**
    - **iOS**: Xcode → "Debug View Hierarchy" button in debug toolbar
    - **Android**: Android Studio → View > Tool Windows > Layout Inspector`,
    
      r8: `## R8 Shrinking on Android
    
    R8 shrinks, optimizes, and obfuscates your APK. Enable in production:
    
    \`\`\`groovy
    // android/app/build.gradle
    def enableProguardInReleaseBuilds = true
    
    android {
      buildTypes {
        release {
          minifyEnabled true
          shrinkResources true
        }
      }
    }
    \`\`\`
    
    **Result:** Sample app shrank from 9.5 MB to 6.3 MB (33% reduction).
    
    Add ProGuard rules for libraries using reflection:
    \`\`\`
    # android/app/proguard-rules.pro
    -keep class io.invertase.firebase.** { *; }
    -dontwarn io.invertase.firebase.**
    \`\`\``,
    
      checklist: `## Common Memory Leak Sources Checklist
    
    - [ ] \`useEffect\` with event listeners missing cleanup
    - [ ] \`setInterval\`/\`setTimeout\` not cleared on unmount
    - [ ] Promises continuing after component unmount (use AbortController)
    - [ ] Closures capturing entire objects instead of specific values
    - [ ] Image cache growing unbounded (configure \`cachePolicy\` on expo-image)
    - [ ] Animation shared values holding references to large data structures
    - [ ] Native event subscriptions not removed (AppState, Keyboard, Dimensions)`,
    };
    
    // ─── Compact sections (rules only, no code) ─────────────────────────
    
    const compactSections: Record<string, string> = {
      'useeffect-cleanup': `## useEffect Cleanup
    - EVERY useEffect with side effects MUST return a cleanup function
    - Cleanup runs on unmount and before re-running the effect`,
    
      'event-listeners': `## Event Listeners
    - Always \`.remove()\` subscriptions in cleanup: AppState, Keyboard, Dimensions
    - Store subscription reference: \`const sub = addEventListener(...)\``,
    
      timers: `## Timers
    - \`clearInterval(id)\` and \`clearTimeout(id)\` in cleanup
    - Never leave timers running after unmount`,
    
      closures: `## Closures
    - Capture specific values, not entire objects: \`const userId = user.id\`
    - Prevents keeping stale references alive`,
    
      'devtools-profiler': `## Memory Profiler
    - React Native DevTools → Memory tab → Heap snapshots
    - Compare snapshots before/after navigation
    - Red flag: objects from unmounted screens still in memory`,
    
      'frame-budget': `## Frame Budget
    - 16ms per frame (60 FPS) / 8ms (120 FPS)
    - JS thread: logic, state, reconciliation
    - UI thread: layout, painting, touch
    - Defer heavy work with InteractionManager — see \`get-performance-patterns\``,
    
      'view-flattening': `## View Flattening
    - RN auto-flattens layout-only views (New Architecture)
    - Use \`collapsable={false}\` to prevent unwanted flattening
    - Debug: Xcode View Hierarchy (iOS), Layout Inspector (Android)`,
    
      r8: `## R8 (Android)
    - Enable \`minifyEnabled true\` + \`shrinkResources true\` for release builds
    - Add ProGuard keep rules for reflection-using libraries
    - ~33% APK size reduction`,
    
      checklist: `## Leak Checklist
    - [ ] useEffect cleanup for listeners/timers
    - [ ] AbortController for async operations
    - [ ] Specific value captures in closures
    - [ ] expo-image cachePolicy configured
    - [ ] Native subscriptions removed`,
    };
    
    // ─── Export ──────────────────────────────────────────────────────────
    
    const pattern: PatternSections = {
      title: 'Memory Optimization',
      sections,
      compactSections,
    };
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions the tool returns content about specific topics (e.g., useEffect cleanup, closures) and that the 'compact' parameter affects output length. However, it doesn't disclose behavioral traits like response format, potential errors, rate limits, or whether it's a read-only operation. It adds some context but lacks comprehensive behavioral details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the main purpose and usage context, followed by examples of topics and a note on parameters. It's relatively concise but could be slightly tighter by integrating the parameter guidance more seamlessly. Most sentences earn their place by providing useful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description provides adequate context for a read-oriented tool with well-documented parameters. It covers purpose, usage, and topics, but lacks details on output format, error handling, or integration with sibling tools. It's minimally complete but has gaps in behavioral transparency.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters thoroughly. The description adds minimal value beyond the schema: it mentions using 'topic' to get a specific section and implies 'compact' reduces content length. This meets the baseline for high schema coverage without significant enhancement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get memory optimization patterns.' It specifies the resource (patterns) and context (memory optimization). However, it doesn't explicitly differentiate from sibling tools like 'get-performance-patterns' or 'get-api-patterns' beyond mentioning memory-specific topics.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage guidance: 'Call this when debugging memory leaks or performance issues.' It also mentions using the 'topic' parameter to get specific sections, which helps guide parameter usage. The context is clear and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ZaharGusyatin/react-native-expo-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server