Logo

Termux-Playwright

v1.61.3 (Dual Engine)
PyPI (Python) npm (Node.js) 💖 Sponsor GitHub Repository

Dual-Engine Architecture: Node.js & Memory Management

Deep-dive on CPython vs V8 Garbage Collection, libuv stream buffers, and Android LMK survival strategies.

1. CPython vs V8 Memory & GC Divergence on Android

Python relies on deterministic Reference Counting to immediately free memory upon scope exit. In contrast, Node.js V8 uses Generational Scavenge & Mark-Sweep-Compact with Lazy GC, keeping heap allocated until pressure builds. On mobile devices with 1GB-4GB RAM, default V8 heap limits (1.4GB) trigger Android Low Memory Killer (LMK) execution.

Dimension CPython Runtime (Python) V8 Engine Runtime (Node.js) Mobile Android Termux Impact
GC Trigger Deterministic Ref-Count (0-sec deallocation) Generational Lazy GC (waits for heap threshold) Node.js requires explicit memory caps
Default Heap Cap OS-governed dynamic RAM 1.4 GB ~ 4 GB desktop default Can trigger Android LMK OOM on <=4GB phones
Exit Lifecycle Synchronous / async exit hooks allowed Event loop is dead inside process.on('exit') Reaper MUST use 100% sync C-syscalls
Crash Propagation Traceback on unhandled exception Unhandled Promise rejection can kill process Requires unhandledRejection global guard

2. Hardened Runtime Protections (Audit Actions Applied)

🛡️ Synchronous Signal & Exit Reaper

In Node.js, process.on("exit") permanently shuts down the event loop—async calls are ignored. ProcessReaper uses pure synchronous C-level process.kill and fs.unlinkSync to guarantee zero zombie leaks.

🛡️ Uncaught Crash Handlers (uncaughtException & unhandledRejection)

Unhandled Promise rejections and uncaught exceptions automatically trigger synchronous ProcessReaper.killAllTracked() before process termination.

🛡️ V8 Heap Capping & forceGarbageCollection()

Low-memory mode caps V8 heap at 128MB. The forceGarbageCollection() helper flushes V8 young/old generation heaps during long-running crawler cycles.

3. Node.js / TypeScript Production Recipes

JavaScript (ESM / CommonJS):

const { launch, setupStealthContext, blockHeavyResources, forceGarbageCollection } = require('termux-playwright');

async function main() {
    // 1. Launch with low memory mode & WakeLock
    const browser = await launch({
        headless: true,
        stealth: true,
        lowMemoryMode: true,
        wakeLock: true
    });

    try {
        const context = await setupStealthContext(browser, {
            locale: 'en-US',
            timezoneId: 'America/New_York'
        });

        const page = await context.newPage();
        
        // 2. Abort heavy media to save mobile data & CPU
        await blockHeavyResources(page, { images: true, media: true, fonts: true });

        await page.goto('https://news.ycombinator.com', { timeout: 45000, waitUntil: 'domcontentloaded' });
        console.log('Page Title:', await page.title());

        // 3. Periodic memory purge for long-running scrapers
        forceGarbageCollection();
    } finally {
        await browser.close();
    }
}

main().catch(console.error);

4. 24/7 Unattended Mobile Daemon with PM2

To run your Node.js crawler 24/7 in Termux without process teardown when Termux is backgrounded, use PM2:

# Install PM2 globally in Termux
npm install -g pm2

# Start crawler with V8 memory cap and auto-restart
pm2 start app.js --name "mobile-crawler" --node-args="--max-old-space-size=256 --expose-gc"

# View live logs & memory
pm2 logs mobile-crawler
pm2 monit