modern-react-qr-reader icon indicating copy to clipboard operation
modern-react-qr-reader copied to clipboard

Chrome: out of memory due to web worker. Chrome not kicking off GC

Open ch08532 opened this issue 3 years ago • 0 comments

cross posted on JodusNodus/react-qr-reader just in case

Not sure if anybody is seeing this issue but I'll raise it here.

We've been using this component in one of our departmental COVID contact tracing apps. We built a very simple web app that runs hours on end scanning qr codes and configured on an android tablet. As part of this app, the react-qr-code component runs scans at 2hz (delay = 500 ms).

Recently, we have done a tablet update and noticed our app now will occasionally run out of memory. We have also confirmed this on Chrome (latest build on Win10). After hours of digging I have traced it down to the web worker with the allocation of the jsQR object. The Chrome version that worked without issue is version 85.0.4183. So from that version to now the gc algo must have changed.

Now, when I start up Chrome dev tools and manually perform a gc then memory is returned and all is normal.

We have also pulled the forked this version of the react-qr-reader (modern-react-qr-reader) and the problem exists there as well.

The only fix (hack?) that worked for us is to periodically bounce the web worker which forces the gc call.

example in the scan function (I modified the lib/index.js in the node_modules folder directly):

 let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
         
        // Recreate web worker
        if (!this.worker) {
          this.worker = new Worker(URL.createObjectURL(workerBlob));
          this.worker.onmessage = this.handleWorkerMessage;
        }
        //post
​       this.worker.postMessage(imageData);

and updated the handleWorkerMessage function to terminate the worker on every 50th scan interval:

if (!legacyMode && typeof delay == 'number' && this.worker) {

        //chrome gc not firing so this is the hack to force gc to run
        if (count % 50 === 0) {
        this.worker.terminate();
        this.worker = undefined;
        count = 0;
        }
        count++;
        this.timeout = setTimeout(this.check, delay);
      }

I've also looked at the demo site and if you watch the task manager the memory does in fact grow but it appears the gc kicks in a bit more reliably.

Also, we changed the web worker postMessage call to use a transferrable object but that too did not solve the problem.

ex:

   let r = new ArrayBuffer(8);
   r = imageData.data.buffer
   this.worker.postMessage(r, [ r ]);

and in the webworker changed:

var e=jsQR(new Uint8ClampedArray(o.data),600,600); //hardcoded width and height for testing

Overall, the problem really only manifests if running for over 1/2 hr or so..sometimes longer sometimes shorter...all dependent on when the gc kicks off.

ch08532 avatar Sep 22 '21 16:09 ch08532

Can I Work on this Issue

Atharva1520 avatar Oct 12 '22 13:10 Atharva1520

yeah.. no problem

aryamangodara avatar Oct 13 '22 04:10 aryamangodara

This issue has not been solved for a long time so can I work on this issue?

SakshamSahu avatar Aug 03 '23 11:08 SakshamSahu