Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory Leak #1121

Open
dissy123 opened this issue Sep 5, 2024 · 6 comments
Open

Memory Leak #1121

dissy123 opened this issue Sep 5, 2024 · 6 comments

Comments

@dissy123
Copy link

dissy123 commented Sep 5, 2024

Describe the bug
I am having again a memory leak :/

When I disable booster-image like this:
image

I get this Result

image

between the snapshot i ran

ab -n 100 -c 10 "http://localhost:3000/"

and garbage collected.

When i enable the import i get:

image

Tested with Version 3.1.6.next.3

Expected behavior
no leak

Desktop (please complete the following information):

  • OS: macOS
  • Browser chrome
  • Version latest
@ThornWalli
Copy link
Contributor

Schlecht…

Can you narrow it down with a previous version?

@dissy123
Copy link
Author

dissy123 commented Sep 5, 2024

this also happend in this version 3.1.4-next.4 , but it was smaller, and i thought it would be from pinia, because the strings in the heap are the ones from the pinia store, so this time its not the blob objects

@dissy123
Copy link
Author

dissy123 commented Sep 6, 2024

image

Again when i comment that out the memory leak is gone.

image

@ThornWalli
Copy link
Contributor

ThornWalli commented Sep 7, 2024

So let's start again :)

How many images do you have integrated? and how large (file size) is the image it wants to load?

  1. problem occurs in the browser.
  2. when calling getMeta on the class Source.
  3. then $booster.getImageSize is called in getMeta so that the width and height of the image can be retrieved once.

Recently we had the server variant in mind e.g. URL.revokeObjectURL(objectUrl) to empty in the generate.

The question now is whether this call is the reason:

  if (!dimensionCache.has(src)) {
    const { width, height } = await new Promise((resolve) => {
      const img = new global.Image();
      img.onload = () => resolve({width: img.naturalWidth, height: img.naturalHeight});
      img.src = src;
    });
    dimensionCache.set(src, { width, height });
  }
  return dimensionCache.get(src)

Could you replace the plugin.tmpl.js in the module? Do not call anything in the client.

export default options => {
  let code = `import { defineNuxtPlugin, useBoosterHydrate } from '#imports';
import vFont from '#booster/directives/vFont';
import { isSupportedBrowser } from '#booster/utils/browser';
import FontList from '#booster/classes/FontList';
import { useNuxtApp, useBoosterHead, useRequestHeaders, useRequestURL, useRequestFetch } from '#imports';
import './fonts.css';`;

  if (options.mode !== 'client') {
    code += `
import NodeCache from 'node-cache';
`;
  }

  code += `

export default defineNuxtPlugin({
  name: 'booster-plugin',
  enforce: 'post',
  async setup(nuxtApp) {

    const hydrate = useBoosterHydrate();

    const fontConfig = await import('./fontConfig').then(
      module => module.default || module
    );
    const fontList = new FontList(fontConfig);

    const head = useBoosterHead();

    nuxtApp.provide('booster', {
      head,
      getImageSize,
      hydrate,
      getFont: fontList.getFont.bind(fontList),
      crossorigin: ${
        options.crossorigin ? "'" + options.crossorigin + "'" : null
      },
      isBrowserSupported: () => isSupportedBrowser(${
        options.supportedBrowserDetector
      }),
      targetFormats: ${JSON.stringify(options.targetFormats)},
      densities: ${JSON.stringify(options.densities)}
    });

  },
  hooks: {
    'app:created'() {
      const { vueApp } = useNuxtApp();
      vueApp.use(vFont);
    }
  }
});

`;

  if (options.mode === 'client') {
    code += `
const dimensionCache = new Map();`;
  } else {
    code += `
const dimensionCache = new NodeCache({  useClones: false, ...${JSON.stringify(options.imageSizeCache)} });`;
  }

  code += `
async function getImageSize (src) {
`;

  if (options.mode === 'client') {
    code += `

    return { width: 0, height: 0 };`;
  } else {
    code += `
  const isNitroPrerender = 'x-nitro-prerender' in useRequestHeaders()

  try {
    let url = src;
    if (isNitroPrerender) {
      url = url.replace(useRequestURL().origin, '');
    }

    if (!dimensionCache.has(url)) {
      const blob = await useRequestFetch()(url);
      const { imageMeta } = await import('image-meta').then(
        module => module.default || module
      );

      const objectUrl = URL.createObjectURL(blob);
      const dimension = await fetch(objectUrl)
      .then(res => res.arrayBuffer())
      .then(Buffer.from)
      .then(imageMeta);

      URL.revokeObjectURL(objectUrl);

      dimensionCache.set(url, dimension);
    }

    return dimensionCache.get(url);
  } catch (error) {
    console.error('getImageSize: ' + src, error);
    return { width: 0, height: 0 };
  }
`;
  }

  code += `
}
`;

  return code;
};

@dissy123
Copy link
Author

dissy123 commented Sep 9, 2024

Hi!

I replaced the tmpl code on my local package of nuxt-booster, made a npm package in my registry out of it and installed it.
Memory Leak is also there
image

I also tried to remove the getImageSize method call also like this:

image

but it didn't change

image

the called image urls:
the images are normal size between 50kb and 1 MB
image

one test url
https://content.local-flow.cloud/api/assets/64723420-6fb6-42d9-85b3-3c1bc79db0da?width=3842&height&mode=CropUpsize&quality=80&format=WEBP&cache=31536000

and there was someone mentioning useHead()
nuxt/nuxt#28690

@ThornWalli
Copy link
Contributor

@dissy123 For the case useHead you could comment out the line:

useHead(() => {
return headData.value;
});

Set a noscript tag and styles in the head for each image.

Link preloads are still added on the server side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants