با استفاده از ()meterUserAgentSpecificMemory، کل میزان مصرف حافظه صفحه وب خود را نظارت کنید.

یاد بگیرید که چگونه میزان استفاده از حافظه صفحه وب خود را در تولید اندازه گیری کنید تا رگرسیون ها را تشخیص دهید.

برندن کنی
Brendan Kenny
اولان دگنبایف
Ulan Degenbaev

مرورگرها حافظه صفحات وب را به صورت خودکار مدیریت می کنند. هر زمان که یک صفحه وب یک شی ایجاد می کند، مرورگر تکه ای از حافظه را "زیر سرپوش" برای ذخیره آن شی اختصاص می دهد. از آنجایی که حافظه یک منبع محدود است، مرورگر جمع‌آوری زباله را انجام می‌دهد تا تشخیص دهد که چه زمانی به یک شی دیگر نیاز نیست و تکه حافظه زیرین را آزاد کند.

با این حال، تشخیص کامل نیست، و ثابت شد که تشخیص کامل یک کار غیرممکن است. بنابراین مرورگرها مفهوم "یک شی مورد نیاز است" را با مفهوم "یک شی در دسترس است" تقریب می کنند. اگر صفحه وب نتواند از طریق متغیرهای آن و فیلدهای دیگر اشیاء قابل دسترس به یک شی دسترسی پیدا کند، مرورگر می‌تواند با خیال راحت آن شی را بازیابی کند. تفاوت بین این دو مفهوم منجر به نشت حافظه می شود همانطور که در مثال زیر نشان داده شده است.

const object = {a: new Array(1000), b: new Array(2000)};
setInterval(() => console.log(object.a), 1000);

در اینجا دیگر به آرایه بزرگتر b نیازی نیست، اما مرورگر آن را بازیابی نمی کند زیرا هنوز از طریق object.b در تماس برگشتی قابل دسترسی است. بنابراین حافظه آرایه بزرگتر به بیرون درز می کند.

نشت حافظه در وب شایع است. با فراموش کردن لغو ثبت شنونده رویداد، با گرفتن تصادفی اشیاء از یک iframe، با بستن نکردن کارگر، با انباشتن اشیا در آرایه‌ها و غیره، می‌توان یکی را معرفی کرد. اگر یک صفحه وب دارای نشت حافظه باشد، استفاده از حافظه آن به مرور زمان افزایش می یابد و صفحه وب برای کاربران کند و متورم به نظر می رسد.

اولین قدم برای حل این مشکل، اندازه گیری آن است. API جدید performance.measureUserAgentSpecificMemory() به توسعه دهندگان اجازه می دهد تا میزان استفاده از حافظه صفحات وب خود را در تولید اندازه گیری کنند و بنابراین نشت حافظه را که از طریق آزمایش محلی انجام می شود شناسایی کنند.

تفاوت performance.measureUserAgentSpecificMemory() با API قدیمی performance.memory ؟

اگر با API غیر استاندارد performance.memory موجود آشنا هستید، ممکن است تعجب کنید که API جدید چه تفاوتی با آن دارد. تفاوت اصلی این است که API قدیمی اندازه پشته جاوا اسکریپت را برمی گرداند در حالی که API جدید حافظه استفاده شده توسط صفحه وب را تخمین می زند. این تفاوت زمانی اهمیت پیدا می‌کند که Chrome یک پشته مشابه را با چندین صفحه وب (یا چندین نمونه از یک صفحه وب مشابه) به اشتراک بگذارد. در چنین مواردی، نتیجه API قدیمی ممکن است خودسرانه خاموش شود. از آنجایی که API قدیمی با عبارات خاص پیاده سازی مانند "heap" تعریف شده است، استانداردسازی آن ناامیدکننده است.

تفاوت دیگر این است که API جدید اندازه گیری حافظه را در حین جمع آوری زباله انجام می دهد. این کار باعث کاهش نویز در نتایج می شود، اما ممکن است مدتی طول بکشد تا نتایج حاصل شود. توجه داشته باشید که سایر مرورگرها ممکن است تصمیم بگیرند که API جدید را بدون تکیه بر جمع‌آوری زباله پیاده‌سازی کنند.

موارد استفاده پیشنهادی

استفاده از حافظه یک صفحه وب به زمان رویدادها، اقدامات کاربر و جمع آوری زباله بستگی دارد. به همین دلیل است که API اندازه گیری حافظه برای جمع آوری داده های استفاده از حافظه از تولید در نظر گرفته شده است. نتایج تماس های فردی کمتر مفید است. موارد استفاده مثال:

  • تشخیص رگرسیون در حین عرضه نسخه جدید صفحه وب برای کشف نشت حافظه جدید.
  • A/B یک ویژگی جدید را برای ارزیابی تأثیر حافظه و تشخیص نشت حافظه آزمایش می کند.
  • ارتباط استفاده از حافظه با مدت جلسه برای تأیید وجود یا عدم وجود نشت حافظه.
  • ارتباط استفاده از حافظه با معیارهای کاربر برای درک تأثیر کلی استفاده از حافظه.

سازگاری با مرورگر

پشتیبانی مرورگر

  • کروم: 89.
  • لبه: 89.
  • فایرفاکس: پشتیبانی نمی شود.
  • سافاری: پشتیبانی نمی شود.

منبع

در حال حاضر API فقط در مرورگرهای مبتنی بر Chromium پشتیبانی می‌شود که از Chrome 89 شروع می‌شود. نتیجه API به شدت به پیاده‌سازی وابسته است زیرا مرورگرها روش‌های متفاوتی برای نمایش اشیاء در حافظه و روش‌های متفاوتی برای تخمین مصرف حافظه دارند. اگر حسابداری مناسب خیلی گران یا غیرممکن باشد، ممکن است مرورگرها برخی از مناطق حافظه را از حسابداری حذف کنند. بنابراین، نتایج را نمی توان در بین مرورگرها مقایسه کرد. مقایسه نتایج برای همان مرورگر فقط معنی دار است.

استفاده از performance.measureUserAgentSpecificMemory()

تشخیص ویژگی

عملکرد performance.measureUserAgentSpecificMemory در دسترس نخواهد بود یا ممکن است با یک SecurityError از کار بیفتد اگر محیط اجرا الزامات امنیتی برای جلوگیری از نشت اطلاعات با منبع متقابل را برآورده نکند. این متکی به جداسازی مبدا متقابل است که یک صفحه وب می تواند با تنظیم سرصفحه COOP+COEP آن را فعال کند.

پشتیبانی را می توان در زمان اجرا شناسایی کرد:

if (!window.crossOriginIsolated) {
  console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
} else if (!performance.measureUserAgentSpecificMemory) {
  console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
} else {
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
    } else {
      throw error;
    }
  }
  console.log(result);
}

تست محلی

Chrome اندازه‌گیری حافظه را در حین جمع‌آوری زباله انجام می‌دهد، به این معنی که API وعده نتیجه را بلافاصله حل نمی‌کند و در عوض منتظر جمع‌آوری زباله بعدی می‌ماند.

فراخوانی API باعث جمع آوری زباله پس از مدتی وقفه می شود که در حال حاضر روی 20 ثانیه تنظیم شده است، هرچند ممکن است زودتر اتفاق بیفتد. راه‌اندازی Chrome با پرچم خط فرمان --enable-blink-features='ForceEagerMeasureMemory' زمان‌بندی را به صفر می‌رساند و برای اشکال‌زدایی و آزمایش محلی مفید است.

مثال

استفاده توصیه شده از API برای تعریف یک مانیتور حافظه جهانی است که استفاده از حافظه را از کل صفحه وب نمونه برداری می کند و نتایج را برای تجمیع و تجزیه و تحلیل به سرور ارسال می کند. ساده ترین راه نمونه برداری دوره ای است، برای مثال هر M دقیقه. با این حال، این باعث تعصب به داده ها می شود زیرا ممکن است پیک های حافظه بین نمونه ها رخ دهد.

مثال زیر نشان می دهد که چگونه می توان اندازه گیری های حافظه بی طرفانه را با استفاده از فرآیند پواسون انجام داد، که تضمین می کند که نمونه ها به یک اندازه در هر نقطه از زمان رخ می دهند ( دمو ، منبع ).

ابتدا تابعی را تعریف کنید که اندازه گیری حافظه بعدی را با استفاده از setTimeout() با بازه تصادفی زمان بندی می کند.

function scheduleMeasurement() {
  // Check measurement API is available.
  if (!window.crossOriginIsolated) {
    console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
    console.log('See https://web.dev/coop-coep/ to learn more')
    return;
  }
  if (!performance.measureUserAgentSpecificMemory) {
    console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
    return;
  }
  const interval = measurementInterval();
  console.log(`Running next memory measurement in ${Math.round(interval / 1000)} seconds`);
  setTimeout(performMeasurement, interval);
}

تابع measurementInterval() یک بازه تصادفی را بر حسب میلی ثانیه محاسبه می کند به طوری که به طور متوسط ​​هر پنج دقیقه یک اندازه گیری وجود دارد. اگر به ریاضیات پشت تابع علاقه دارید ، توزیع نمایی را ببینید.

function measurementInterval() {
  const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
  return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}

در نهایت، تابع async performMeasurement() API را فراخوانی می کند، نتیجه را ثبت می کند و اندازه گیری بعدی را برنامه ریزی می کند.

async function performMeasurement() {
  // 1. Invoke performance.measureUserAgentSpecificMemory().
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
      return;
    }
    // Rethrow other errors.
    throw error;
  }
  // 2. Record the result.
  console.log('Memory usage:', result);
  // 3. Schedule the next measurement.
  scheduleMeasurement();
}

در نهایت، اندازه گیری را شروع کنید.

// Start measurements.
scheduleMeasurement();

نتیجه ممکن است به صورت زیر باشد:

// Console output:
{
  bytes: 60_100_000,
  breakdown: [
    {
      bytes: 40_000_000,
      attribution: [{
        url: 'https://example.com/',
        scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 20_000_000,
      attribution: [{
          url: 'https://example.com/iframe',
          container: {
            id: 'iframe-id-attribute',
            src: '/iframe',
          },
          scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 100_000,
      attribution: [],
      types: ['DOM']
    },
  ],
}

تخمین کل مصرف حافظه در قسمت bytes برگردانده می شود. این مقدار بسیار وابسته به پیاده سازی است و نمی توان آن را در بین مرورگرها مقایسه کرد. حتی ممکن است بین نسخه های مختلف یک مرورگر تغییر کند. این مقدار شامل جاوا اسکریپت و حافظه DOM همه iframe ها، ویندوزهای مرتبط، و کارگران وب در فرآیند فعلی است.

لیست breakdown اطلاعات بیشتری در مورد حافظه استفاده شده ارائه می دهد. هر ورودی بخشی از حافظه را توصیف می کند و آن را به مجموعه ای از پنجره ها، iframe ها و کارگرانی که با URL شناسایی شده اند نسبت می دهد. فیلد types ، انواع حافظه مخصوص پیاده سازی مرتبط با حافظه را فهرست می کند.

مهم است که همه لیست ها را به روشی عمومی در نظر بگیرید و فرضیات را بر اساس یک مرورگر خاص کدگذاری نکنید. برای مثال، برخی از مرورگرها ممکن است یک breakdown خالی یا یک attribution خالی را برگردانند. مرورگرهای دیگر ممکن است چندین ورودی را در attribution برگردانند که نشان می دهد آنها نمی توانند تشخیص دهند که کدام یک از این ورودی ها صاحب حافظه هستند.

بازخورد

گروه جامعه عملکرد وب و تیم Chrome دوست دارند درباره افکار و تجربیات شما درباره performance.measureUserAgentSpecificMemory() بشنوند.

در مورد طراحی API به ما بگویید

آیا چیزی در مورد API وجود دارد که مطابق انتظار کار نمی کند؟ یا آیا ویژگی های گم شده ای وجود دارد که برای اجرای ایده خود به آنها نیاز دارید؟ یک مشکل مشخصات را در مخزن GitHub performance.measureUserAgentSpecificMemory() ثبت کنید یا افکار خود را به یک مشکل موجود اضافه کنید.

گزارش مشکل در اجرا

آیا اشکالی در پیاده سازی کروم پیدا کردید؟ یا اجرا با مشخصات متفاوت است؟ یک اشکال را در new.crbug.com ثبت کنید. اطمینان حاصل کنید که تا آنجا که می توانید جزئیات را درج کنید، دستورالعمل های ساده ای را برای بازتولید اشکال ارائه دهید، و Components را روی Blink>PerformanceAPIs تنظیم کنید. Glitch برای به اشتراک گذاری سریع و آسان تکرارها عالی عمل می کند.

نشان دادن حمایت

آیا قصد دارید از performance.measureUserAgentSpecificMemory() استفاده کنید؟ پشتیبانی عمومی شما به تیم Chrome کمک می‌کند ویژگی‌ها را اولویت‌بندی کند و به سایر فروشندگان مرورگر نشان می‌دهد که چقدر حمایت از آنها ضروری است. یک توییت به @ChromiumDev ارسال کنید و به ما اطلاع دهید که کجا و چگونه از آن استفاده می کنید.

لینک های مفید

قدردانی ها

از Domenic Denicola، Yoav Weiss، Mathias Bynens برای بررسی‌های طراحی API، و Dominik Inführ، Hannes Payer، Kentaro Hara، Michael Lippautz برای بررسی کد در Chrome سپاس‌گزاریم. همچنین از Per Parker، Philipp Weis، Olga Belomestnykh، Matthew Bolohan و Neil Mckay برای ارائه بازخورد ارزشمند کاربر که API را تا حد زیادی بهبود بخشید، تشکر می‌کنم.

تصویر قهرمان توسط هریسون برادبنت در Unsplash