Huge memory leak - React

Hello, we have been using @pdftron/pdfjs-express-viewer since last week and we noticed a memory leak.

We encountered this problem while trying to use the Viewer in a Modal that unmounts everytime you close it.

Reproduce method:

  1. Open htop or a similar system to monitor your memory.
  2. Then go to PDF.js Viewer Demo | PDF.js Express
  3. Now, alternate between the demo page and the pricing page.
  4. every time you enter the demo page, your memory will increase.

We are using the getInstance()
We think dispose() is not gc everything.

Hi Carlos1,

Do you have any addEventListeners()?

Ensure you run removeEventListener on any event listeners you have added, these will hold onto the instance in memory if not removed.

As for our demo, we don’t run dispose on our instance since we don’t open/reopen it in the same page.

Best regards,
Tyler

Hi @tgordon

Thank you for your answer, we are running dispose on our side but it doesn’t change anything, so we believe this is a bug from the viewer.

I personally spent the whole day today trying to find a solution for this. Im SUPER available to provide you with more info.

Yes, we have two event listeners, but even when I remove them, we still have the same issue.

This is quite a critical bug for us since after a few uses this crashes the whole browser.

Hi CarlosVergikosk,

  • Can you please provide a sample application that reproduces the issue?
  • And by chance are you calling display: none on the viewer?
  • Or can you provide the code that you use to show/hide/remove the viewer from the webpage?

Best regards,
Tyler

Hi @tgordon ,
Answer to 1: Not really, but I can explain our use-case:

We have a right tab panel that shows the viewer.
We allow our users to open a modal with other documents at the same time.

The modal destroys its content on close, meaning it will create a new web viewer instance on each use.

Answer to 2: No, we totally unmount the viewer from our app.

Answer to 3: Yes, here it is


const instance = getInstance()

React.useEffect(() => {
        return () => {
            if (instance) {
                instance.UI.closeDocument()
                instance.UI.dispose()
            }
        }
    }, [instance])

We tried to call all the methods available after each destroy but there are accumulations of data in memory on every creation of a new instance.

After a few openings, chrome crashes.

The solution of reusing a viewer is not really convenient for us.
Ideally, the disposal function would erase everything in order to keep the memory healthy.

Best,
Carlos

Hi @tgordon, do you have any news?

Hi Carlos,

The implication of checking
if(instance)
means you are storing the instance object, therefore, calling

instance.UI.closeDocument()
instance.UI.dispose()

Does not delete the instance from memory. You have to dereference the instance by (if you are using state to store the instance):

setInstance(null)`

After calling closeDocument and dispose

Best regards,
Tyler

Hi @tgordon, im using React and yes I’m using a local state to keep track of it, but the state is unmounted and so removed together with the viewer. I believe the WebViewer() is actually the problem.

I actually proved it in my example on the topic description, you have the same problem with your pdfjs viewer demo.

Hi CarlosVergikosk,

We will add this to our backlog to be prioritized by our product team. Thank you for your patience.

Best regards,
Tyler

@tgordon Thank you, i hope this can be done relatively quickly as it is very important for us.

7npxae

Hey CarlosVergikosk,

Were looking to release a new version sometime today or tomorrow that hopefully fixes the issue.
You were right in some ways about not garbage collecting everything.

We actually need to convert UI.dispose() into a promise and I would also suggest using instance.Core.documentViewer.dispose() as well.

Keep an eye out for 8.7.4!

Best regards,
Tyler Gordon

Hey @tgordon

Any news on the release?

Hey CarlosVergikosk,

It has been released! Apologies for the delay

Best regards
Tyler

I updated my package “@pdftron/pdfjs-express-viewer” and unfortunatelly i see no difference.

I cant find the changelogs to this release, so I’m a bit confused about what changed and if there’s is something i need to change on my side.

@tgordon
Thanks

Hey CarlosVergikosk,

I didn’t generate a changelog for this version since it was a patch release, basically, we pushed a couple of minor fixes.

  • Use the updated dispose function, which is now a promise
  • Fix Chrome printing extra blank page CSS issue
  • other issues that I wasn’t directly involved in

How are you disposing with the latest version?

Best regards,
Tyler

I’m disposing it like this.

image

I’m not sure if you are familiar with React, but what this does is that the instance disposes when a viewer is unmounted.

I don’t think we can await function on the un-mount function of React, correct me if I’m wrong.
This seems not to work, maybe you can put me in contact with someone from technical support in order to resolve this issue.

Thanks @tgordon

Hey CarlosVergikosk,

Can you try doing:

instance.Core.documentViewer.closeDocument();
await instance.UI.dispose();
await instance.Core.documentViewer.dispose()

We had to convert UI.dispose to a promise since there was a worker not being fully closed.

Best regards,
Tyler

I implemented your suggestion (as a promise) and garbage collection is still a problem.

image

My memory keeps increasing until it crashes the browser and sometimes even freezes the whole computer.

The problem is not solved on our side.

I verified i correctly updated the package, and i can confirm i have the new promise changes on my node-modules source code.

Thanks in advance, @tgordon
Hopefully, we can fix this quickly, its really a critical issue for us.

Hi @tgordon , do you have any news related to this problem?

Hey CarlosVergikosk,

The recent changes fixed the memory leak issue on my end, so Im curious if its an integration issue.

Can you provide a sample project to reproduce?

Best regards,
Tyler