Skip to content

Conversation

nitinmehtaa
Copy link

@nitinmehtaa nitinmehtaa commented Oct 10, 2025

Related Issues

Fixes #16407

What does this PR do?

This PR addresses out-of-memory errors that occur when using 32-bit Chrome with memory-intensive extensions (such as OKX wallet) on Windows. The issue manifests as connection timeouts between Selenium and ChromeDriver when tabs exceed the 1GB per-tab memory limit of 32-bit Chrome.

Bug Description

When using 32-bit Chrome on Windows with large extensions:

  • Extensions can consume over 1GB of memory
  • Opening new tabs triggers out-of-memory errors
  • Selenium loses connection to ChromeDriver
  • Manual page reload triggers garbage collection and temporarily restores connection

Implementation Notes

Root Cause:
32-bit Chrome enforces a per-tab memory limit of approximately 1GB. Memory-intensive extensions combined with new tab creation can exceed this limit, causing renderer crashes and connection failures.

Solution:

  1. Proactive garbage collection - Triggers Python GC at critical points:

    • After closing windows/tabs (close method)
    • Before opening new windows on 32-bit Windows (switch_to_new_window method)
    • After driver shutdown (quit method)
  2. Enhanced session cleanup - Improved quit method:

    • Iteratively closes all window handles before final quit
    • Prevents orphaned windows from holding memory
    • Includes comprehensive exception handling
  3. Platform detection and warnings:

    • Detects 32-bit Chrome on Windows during initialization
    • Logs warning about memory limitations
    • Documents limitation in class docstring
    • Recommends upgrading to 64-bit Chrome

Technical Implementation:

  • Imports: gc, logging, platform modules
  • Method overrides: quit, close, switch_to_new_window
  • Backward compatible: does not break existing functionality
  • Defensive programming: exception handling ensures robustness

Additional Considerations

Testing:

  • Tested with memory-intensive browser extensions
  • Verified backward compatibility with existing code
  • Confirmed GC triggers do not impact performance significantly

Follow-on work:

  • Consider adding memory monitoring utilities in future releases
  • Potential integration with ChromeDriver logs for better diagnostics

Limitations:

  • Does not eliminate the 1GB per-tab limit (Chrome architecture constraint)
  • GC helps but cannot prevent OOM if single tab legitimately needs >1GB
  • Best solution remains upgrading to 64-bit Chrome

Types of changes

  • Bug fix (backwards compatible)

Contributing Guidelines

This contribution follows the project's contributing guidelines, adheres to the code of conduct, and respects the security policy.


PR Type

Bug fix


Description

  • Fix 32-bit Chrome out-of-memory errors with garbage collection

  • Add platform detection and memory limit warnings

  • Enhance session cleanup in quit method

  • Improve memory management for window operations


Diagram Walkthrough

flowchart LR
  A["32-bit Chrome Detection"] --> B["Memory Warning"]
  C["Window Operations"] --> D["Garbage Collection"]
  E["Enhanced Quit"] --> F["Cleanup All Handles"]
  F --> D
Loading

File Walkthrough

Relevant files
Bug fix
webdriver.py
Enhanced Chrome WebDriver with memory management                 

py/selenium/webdriver/chrome/webdriver.py

  • Add imports for gc, logging, and platform modules
  • Override quit(), close(), and switch_to_new_window() methods
  • Add 32-bit Chrome detection with memory warnings
  • Implement proactive garbage collection at critical points
+71/-2   

…ython bindings

This commit addresses out-of-memory errors that occur when using 32-bit Chrome with large extensions (like OKX wallet) on Windows, as reported in issue SeleniumHQ#16407.

Root Cause:
32-bit Chrome has a per-tab memory limit of approximately 1GB. When extensions consume significant memory, opening new tabs can exceed this limit, causing out-of-memory errors and connection timeouts between Selenium and ChromeDriver.

Implemented Solution:
1. Added garbage collection triggers at critical points:
   - After closing windows/tabs (close method)
   - Before opening new windows on 32-bit systems (switch_to_new_window method)
   - After quitting the driver (quit method)

2. Enhanced session cleanup in quit method:
   - Iteratively closes all window handles before final quit
   - Prevents orphaned windows from holding memory
   - Includes exception handling to ensure cleanup completes

3. Added platform detection and warning:
   - Detects 32-bit Chrome on Windows during initialization
   - Logs warning about 1GB per-tab memory limitation
   - Advises upgrading to 64-bit Chrome
   - Includes documentation in class docstring

Technical Details:
- Imports gc, logging, and platform modules
- Overrides quit, close, and switch_to_new_window methods
- Maintains backward compatibility with existing code
- Exception handling ensures robustness

This implementation helps prevent the timeout issues described in the bug report by proactively managing memory and providing clear warnings to users about platform limitations.
@CLAassistant
Copy link

CLAassistant commented Oct 10, 2025

CLA assistant check
All committers have signed the CLA.

@selenium-ci selenium-ci added the C-py Python Bindings label Oct 10, 2025
Copy link
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Exception swallowing

Description: Broad bare except blocks in quit() suppress all exceptions, potentially hiding critical
failures and leaving resources in an inconsistent state.
webdriver.py [72-91]

Referred Code
def quit(self) -> None:
    """Closes the browser and shuts down the ChromeDriver executable
    that is started when starting the ChromeDriver. Includes improved
    cleanup for memory management.
    """
    try:
        # Close all window handles before quit
        if hasattr(self, 'window_handles'):
            try:
                handles = self.window_handles
                for handle in handles[:-1]:  # Keep last handle for quit
                    try:
                        self.switch_to.window(handle)
                        self.close()
                    except Exception:
                        pass
            except Exception:
                pass
    except Exception:
        pass
Environment misdetection

Description: Platform detection relies on Python process architecture rather than actual
Chrome/ChromeDriver bitness, which could misidentify environment and lead to misleading
warnings or behavior.
webdriver.py [65-70]

Referred Code
if platform.system() == "Windows" and platform.architecture()[0] == "32bit":
    logger.warning(
        "Running 32-bit Chrome on Windows. Each tab has a 1GB memory limit. "
        "Out-of-memory errors may occur with large extensions or applications. "
        "Consider upgrading to 64-bit Chrome."
    )
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
The core fix is likely ineffective

The suggestion argues that calling Python's garbage collector (gc.collect())
will not resolve memory issues within the separate Chrome browser process. This
makes the PR's core solution ineffective and suggests reconsidering the
approach.

Examples:

py/selenium/webdriver/chrome/webdriver.py [97]
        gc.collect()
py/selenium/webdriver/chrome/webdriver.py [106]
        gc.collect()

Solution Walkthrough:

Before:

class WebDriver(ChromiumDriver):
    def quit(self) -> None:
        # ... close all windows ...
        super().quit()
        gc.collect()

    def close(self) -> None:
        super().close()
        gc.collect()

    def switch_to_new_window(self, type_hint: str = "tab") -> None:
        if platform.system() == "Windows" and platform.architecture()[0] == "32bit":
            gc.collect()
        super().switch_to_new_window(type_hint)

After:

class WebDriver(ChromiumDriver):
    # The suggestion implies removing gc.collect() as it is ineffective
    # for managing Chrome's memory.

    def quit(self) -> None:
        # ... close all windows ...
        super().quit()
        # No gc.collect()

    def close(self) -> None:
        super().close()
        # No gc.collect()

    def switch_to_new_window(self, type_hint: str = "tab") -> None:
        # No gc.collect()
        super().switch_to_new_window(type_hint)
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a fundamental flaw in the PR's logic: Python's gc.collect() manages memory for the Python process, not the separate Chrome browser process where the out-of-memory errors occur, rendering the core fix ineffective.

High
Possible issue
Avoid silently swallowing all exceptions

In the quit method, replace the silent try...except Exception: pass blocks with
exception logging to make potential issues during window closing visible for
debugging.

py/selenium/webdriver/chrome/webdriver.py [77-91]

-try:
-    # Close all window handles before quit
-    if hasattr(self, 'window_handles'):
-        try:
-            handles = self.window_handles
-            for handle in handles[:-1]:  # Keep last handle for quit
-                try:
-                    self.switch_to.window(handle)
-                    self.close()
-                except Exception:
-                    pass
-        except Exception:
-            pass
-except Exception:
-    pass
+# Best-effort attempt to close all but the last window.
+if hasattr(self, "window_handles"):
+    try:
+        handles = self.window_handles
+        # The last handle is closed by the quit command.
+        for handle in handles[:-1]:
+            try:
+                self.switch_to.window(handle)
+                self.close()
+            except Exception as e:
+                logger.warning("Ignoring exception while closing window '%s': %s", handle, e)
+    except Exception as e:
+        logger.warning("Ignoring exception while trying to close extra windows: %s", e)
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that silently swallowing exceptions is poor practice and proposes logging them, which significantly improves the code's robustness and debuggability.

Low
General
Avoid repeating the platform check

Avoid repeating the platform check in init and switch_to_new_window by
performing it once in init and storing the result in an instance attribute
for reuse.

py/selenium/webdriver/chrome/webdriver.py [64-70]

-# Log warning for 32-bit Chrome on Windows
-if platform.system() == "Windows" and platform.architecture()[0] == "32bit":
+# Check for 32-bit Chrome on Windows and log warning
+self._is_32bit_win = platform.system() == "Windows" and platform.architecture()[0] == "32bit"
+if self._is_32bit_win:
     logger.warning(
         "Running 32-bit Chrome on Windows. Each tab has a 1GB memory limit. "
         "Out-of-memory errors may occur with large extensions or applications. "
         "Consider upgrading to 64-bit Chrome."
     )
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion improves code quality by applying the DRY principle, making the code cleaner and slightly more efficient by avoiding a repeated platform check.

Low
Learned
best practice
Keep docstring API-focused

Move runtime/environment warnings to logs or official documentation, and keep
the class docstring concise and API-focused to prevent stale or misleading
guidance.

py/selenium/webdriver/chrome/webdriver.py [31-38]

 class WebDriver(ChromiumDriver):
-    """Controls the ChromeDriver and allows you to drive the browser.
+    """Controls the ChromeDriver and allows you to drive the browser."""
     
-    WARNING: When using 32-bit Chrome on Windows, each tab has a memory limit
-    of approximately 1GB. Large extensions or web applications may exceed this
-    limit causing out-of-memory errors. Consider using 64-bit Chrome or
-    manually managing tab lifecycles and triggering garbage collection.
-    """
+    def __init__(
+        self,
+        options: Optional[Options] = None,
+        service: Optional[Service] = None,
+        keep_alive: bool = True,
+    ) -> None:
+        service = service if service else Service()
+        options = options if options else Options()
+        super().__init__(
+            browser_name=DesiredCapabilities.CHROME["browserName"],
+            vendor_prefix="goog",
+            options=options,
+            service=service,
+            keep_alive=keep_alive,
+        )
+        if platform.system() == "Windows" and platform.architecture()[0] == "32bit":
+            logger.warning(
+                "Detected 32-bit Chrome on Windows; memory constraints may cause OOM. Consider 64-bit Chrome."
+            )
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Keep API and documentation accurate and consistent; avoid adding non-authoritative operational warnings to class docstrings that may become outdated or mislead users.

Low
  • More

Copy link
Member

@cgoldberg cgoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to force garbage collection, you can do it in your own subclass of webdriver, but we aren't adding this... sorry.

@cgoldberg cgoldberg closed this Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🐛 Bug]: out of memory, 32bit chrome

4 participants