Чи використовувати хеш-таблицю для збору сміття вирішить зупинити світову проблему розмітки та розміщення?


13

У алгоритмі збору сміття-компактного збору сміття вам потрібно зупинити світ при переміщенні об'єктів, оскільки опорний графік стає непослідовним, і вам доведеться замінити значення всіх посилань, що вказують на об’єкт.

Але що робити, якщо у вас була хеш-таблиця з ідентифікатором об'єкта як ключовим, а вказівник як значенням, а посилання вказували б на вказаний ідентифікатор замість адреси об’єкта ... тоді для виправлення посилань буде потрібно лише зміна одного значення, а пауза буде потрібна лише у випадку, якщо об'єкт намагається записати під час копіювання ...

Чи є помилка в моїй думці?

Відповіді:


19

Оновлення посилань - не єдине, що вимагає паузи. Стандартні алгоритми, як правило, згруповані під "позначенням", припускають, що весь графік об'єкта залишається незмінним під час його маркування. Правильна обробка модифікацій (створені нові об'єкти, змінені посилання) вимагає досить складних альтернативних алгоритмів, як, наприклад, триколірний алгоритм. Термін парасольки - це "одночасне вивезення сміття".

Але так, оновлення посилань після ущільнення також потребує паузи. І так, використання непрямості (наприклад, через постійний ідентифікатор об’єкта та хеш-таблицю до реальних покажчиків) може значно зменшити паузу. Можливо, навіть можна зробити цю частину незаблокованою, якщо бажаєте. Як і раніше, було б настільки ж складно, як будь-яка одночасність спільної пам’яті низького рівня, але немає принципових причин, щоб вона не працювала.

Однак це матиме серйозні недоліки. Крім зайвого місця ( принаймні, два зайвих слова для всіх об’єктів), це робить кожну деребацію значно дорожчою. Навіть щось таке просте, як отримання атрибуту, тепер передбачає повний пошук хеш-таблиці. Я б оцінив, що показник ефективності набагато гірший, ніж для поступового відстеження.


Ну, у нас сьогодні багато пам’яті, тому ми могли б сказати таблицю 50 Мб і хеш може бути простим модулем, тому лише одна інструкція ...
mrpyo

3
@mrpyo отримання розміру хеш-таблиці, операція по модулю, затримка зсуву хеш-таблиці, щоб отримати фактичний вказівник об'єкта, дереференція до самого об'єкта. Плюс можливо, перетасовка регістрів. Ми закінчуємо інструкціями 4+. Також у цієї схеми є проблеми, що стосуються локальності пам'яті: тепер і хеш-таблиця, і самі дані повинні вміщуватися в кеш-пам'ять.
амон

@mrpyo Вам потрібен один запис (ідентифікатор об’єкта -> поточна адреса) на об’єкт, правда? І незалежно від того, як дешево хеш - функція, ви будете мати зіткнення і необхідність їх вирішення. Також те, що сказав Амон.

@amon - лише питання часу, перш ніж у процесорів буде розміщено кеш-пам'ять 50 Мб або більше :)
Móż

1
@ Ӎσᶎ На той момент ми можемо поставити транзистори на 50 МіБ на мікросхемі і ще затримати достатньо низький рівень, щоб він міг працювати як кеш L1 або L2 (кеш-пам’яті L3 вже розміром до 15 МБ, але, як правило, без чіпа AFAIK і далеко гірші затримки, ніж L1 та L2), відповідно до цього ми матимемо величезну кількість основної пам’яті (та даних, що їх потрібно вносити). Таблиця не може бути фіксованого розміру, вона повинна рости разом із купою.

19

Всі проблеми в інформатиці можуть бути вирішені іншим рівнем непрямості ... за винятком проблеми занадто багато шарів опосередкованості

Ваш підхід не відразу вирішує проблему вивезення сміття, а лише переміщує її на один рівень. І якою ціною! Тепер кожен доступ до пам'яті проходить через іншу затримку покажчика. Ми не можемо кешувати результат результату, оскільки воно, тим часом, було переміщено, ми завжди мусимо пройти ідентифікатор об'єкта. У більшості систем це непряме неприйнятне, і для зупинки світу передбачається менша загальна вартість виконання.

Я сказав, що ваша пропозиція лише рухає проблему, а не вирішує її. Проблема полягає у повторному використанні ідентифікаторів об’єктів. Ідентифікатори об'єктів тепер є нашим еквівалентом покажчиків, і є лише обмежена кількість адрес. Можливо (особливо в 32-бітовій системі), що протягом життя вашої програми буде створено більше об'єктів INT_MAX, наприклад в циклі, як

while (true) {
    Object garbage = new Object();
}

Якщо ми просто збільшимо ідентифікатор об'єкта для кожного об’єкта, у нас в якийсь момент не вистачить ідентифікаторів. Тому ми повинні з'ясувати, які посвідчення особи досі використовуються, а які безкоштовні, щоб їх можна було повернути. Звучить знайомо? Зараз ми повернулися до першого.


Можна імовірно використовувати ідентифікатори, які просто "досить великі", скажімо, 256 бітових бінтумів? Я не кажу, що ця ідея в цілому хороша, але ви майже напевно можете обійти повторне використання IDS.
Vality

@ Reality реально так - наскільки ми бачимо, це може вирішити проблему повторного використання ID. Але це лише черговий аргумент "640K для когось вистачить", і це фактично не вирішує проблему. Більш катастрофічний аспект полягає в тому, що розмір усіх об'єктів (і хеш-таблиці) повинен був збільшитися, щоб розмістити ці великі псевдо-покажчики, і що під час хеш-доступу нам потрібно порівнювати цей bigint з іншими ідентифікаторами, які, ймовірно, зависять кілька регістрів , і візьміть кілька інструкцій для завершення (на 64-розрядному: 8 × завантаження, 4 × порівняння, 3 ×, а це 5-кратне збільшення порівняно з нативними вставками).
амон

Так, у вас буде ідентифікаторів через деякий час, і вам потрібно буде змінити всі, для чого потрібна пауза. Але, можливо, це була б рідкісна подія ...
mrpyo

@amon Дуже погодився, всі дуже хороші моменти є, набагато краще мати справді стійку систему, я згоден. Це буде нестерпно повільним, що б ви не робили, це все одно цікаво лише в теорії. Особисто я все-таки не великий фанат
збору

@amon: у світі є більше коду, ніж просто це, що піде не так, як тільки ви обмотаєте 64-бітний ідентифікатор (584 роки наносекунд, і ви, ймовірно, можете домовитись про розподіл пам'яті, щоб зайняти 1ns, особливо якщо ви не поділите глобальний лічильник що виплюває ідентифікатори!). Але впевнено, якщо вам не потрібно на це покладатися, то вам цього не потрібно.
Стів Джессоп

12

У вашій думці немає помилок, ви щойно описали щось дуже близьке до того, як працював оригінальний збирач сміття Java

Оригінальна віртуальна машина Java [6] та деякі віртуальні машини Smalltalk використовують непрямі вказівники, звані ручками в [6], для позначення об’єктів. Ручки дозволяють легко переміщувати предмети під час вивезення сміття, оскільки з ручками існує лише один прямий вказівник на кожен об’єкт: той, що знаходиться в його ручці. Всі інші посилання на об'єкт опосередковано через хан-ле. У таких системах пам’яті на основі ручки, хоча адреси об’єктів змінюються протягом життя об'єктів і тому їх не можна використовувати для хешування, адреси оброблюваної адреси залишаються постійними.

Простір та економне захоплення об'єктів, зібраних сміттям

У поточній реалізації Sun віртуальної машини Java посилання на екземпляр класу - це вказівник на ручку, яка сама є парою покажчиків: одна на таблицю, що містить методи об'єкта, і вказівник на об'єкт Class, який представляє тип об'єкта, а інший - в пам'ять, виділену з кучі Java для даних об’єкта.

Специфікація віртуальної машини Java (1997)

Тож це спрацьовує, це було випробувано, а його неефективність призвела до розвитку систем генерації знаків та зачисток.


Імовірно, ці ручки не були ключами у хештайлі (як у питанні), хоча? Тут немає потреби, просто структура, що містить вказівник. Тоді ручки мають однаковий розмір, тому їх можна виділити з розподільника купи. Який за своєю природою не потребує внутрішнього ущільнення, оскільки не роздроблений. Ви можете оплакувати нездатність великих блоків, які використовує цей розподільник, бути самостійно переселеними. Який можна вирішити іншим рівнем непрямості ;-)
Стів Джессоп

@SteveJessop так, у реалізації gc не було хештеля, хоча значенням ручки було також значення, поверненеObject.getHashCode()
Pete Kirkham
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.