diff --git a/components/micropython/vendor/ports/esp32/gccollect.c b/components/micropython/vendor/ports/esp32/gccollect.c
index e16e8028adb1e58145c22b73e8b1679fc9028693..afded5129bb06e63a57cff1e62721e169c839e0b 100644
--- a/components/micropython/vendor/ports/esp32/gccollect.c
+++ b/components/micropython/vendor/ports/esp32/gccollect.c
@@ -39,30 +39,25 @@
 
 #include "xtensa/hal.h"
 
-static void gc_collect_inner(int level) {
+// The level argument must be volatile to force the compiler to emit code that
+// will call this function recursively, to nest the C stack.
+static void gc_collect_inner(volatile unsigned int level) {
     if (level < XCHAL_NUM_AREGS / 8) {
+        // Go deeper on the stack to spill more registers from the register window.
         gc_collect_inner(level + 1);
-        if (level != 0) {
-            return;
-        }
-    }
-
-    if (level == XCHAL_NUM_AREGS / 8) {
-        // get the sp
+    } else {
+        // Deep enough so that all registers are on the C stack, now trace the stack.
         volatile uint32_t sp = (uint32_t)esp_cpu_get_sp();
         gc_collect_root((void **)sp, ((mp_uint_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t));
-        return;
     }
-
-    // trace root pointers from any threads
-    #if MICROPY_PY_THREAD
-    mp_thread_gc_others();
-    #endif
 }
 
 void gc_collect(void) {
     gc_collect_start();
     gc_collect_inner(0);
+    #if MICROPY_PY_THREAD
+    mp_thread_gc_others();
+    #endif
     gc_collect_end();
 }
 
@@ -90,3 +85,4 @@ size_t gc_get_max_new_split(void) {
 }
 
 #endif
+