2009-11-18 3 views
2

В Java каждый объект имеет монитор синхронизации. Поэтому я предполагаю, что реализация довольно сжата с точки зрения использования памяти и, надеюсь, будет быстрой.Какова самая эффективная реализация java-монитора объектов на C++?

При переносе на C++ то, что было бы лучшей реализацией для него. Я думаю, что должно быть что-то лучше, чем «pthread_mutex_init», или это накладные расходы на Java в действительности настолько высоки?

Редактировать: я только что проверил, что pthread_mutex_t на Linux i386 имеет размер 24 байта. Thats огромный, если я должен зарезервировать это пространство для каждого объекта.

+0

Возможно, вам захочется указать, с какой платформы вы работаете, поскольку потоки в C++ обязательно зависят от платформы. Решение с использованием pthreads будет отличаться от решения с использованием функций синхронизации Win32. –

+0

Ну все основные платформы - MacOSX, Solaris, Windows, Linux. – Lothar

ответ

2

The Sun Hotspot JVM implements thin locks using compare and swap. Если объект заблокирован, то ожидающий поток ожидает на мониторе потока, который заблокировал объект. Это означает, что вам нужен только один тяжелый замок на поток.

+0

Это то, что нам нравится. Все указатели должны иметь нижние три бита, повторно используемые для флагов ;-) –

+0

Это означает, что класс должен будет поддерживать список потоков для пробуждения, а затем ударять каждый из них, когда вызывается уведомление. Эта реализация может быть такой же быстрой, как любая система, но не с pthreads. –

3

В некотором смысле это хуже, чем pthread_mutex_init, на самом деле. Из-за того, что Java ожидает/уведомляет вас о необходимости частичной мьютексы и переменной условия для реализации монитора.

На практике при внедрении JVM вы выслеживаете и применяете каждую отдельную оптимизацию для конкретной платформы в книге, а затем придумываете новые, чтобы сделать мониторы как можно быстрее. Если вы не можете сделать действительно дьявольскую работу, вы определенно не в состоянии оптимизировать сбор мусора ;-)

Одно наблюдение заключается в том, что не каждый объект должен иметь свой собственный монитор. Объект, который в настоящий момент не синхронизирован, не нуждается в нем. Таким образом, JVM может создавать пул мониторов, и каждый объект может иметь только поле указателя, которое заполняется, когда поток фактически хочет синхронизировать объект (например, с помощью операции атомного сравнения и операции свопинга для конкретной платформы). Таким образом, стоимость инициализации монитора не должна увеличивать стоимость создания объекта. Предполагая, что память предварительно очищена, создание объекта может быть: декремент указателя (плюс некоторая проверка границ, с предсказанной ложной ветвью к коду, который запускает gc и т. Д.); заполните тип; вызов самого производного конструктора. Я думаю, вы можете организовать для конструктора Object ничего не делать, но, очевидно, многое зависит от реализации.

На практике среднее Java-приложение не синхронизируется на очень многих объектах в любой момент времени, поэтому пулы мониторинга потенциально представляют собой огромную оптимизацию во времени и в памяти.

+0

Если у вас много плохого кода программистов, который добавляет синхронизацию с каждым методом. Но мне нравится твоя идея. Торговля хеш-таблицей, чтобы найти связанный объект с памятью. – Lothar

+0

В моем (довольно старом) опыте реализации JVM плохие кодеры также гарантируют, что большинство объектов, созданных их программами, являются Strings. Поэтому, даже если они синхронизируют все свои собственные методы и как-то избегают взаимоблокировок, это все еще только меньшинство всех объектов, которым нужен монитор ;-). Я не думаю, что наша JVM использовала тонкую блокировку, как в ссылке brianegge: объект либо имел монитор, либо нет. В то время мы были быстрее Солнца (для PJava/J2ME), но это было до того, как умные вещи от HotSpot достигли мобильных устройств, и KVM стал ужасающим. –

2

Я не уверен, как это делает Java, но .NET не поддерживает мьютекс (или аналог - структура, которая содержит его, называется «syncblk») непосредственно в объекте. Скорее, он имеет глобальную таблицу синхроимпульсов, а объект ссылается на свою синхронизацию по индексу в этой таблице. Кроме того, объекты не получают синхронизацию, как только они создаются - вместо этого она создается по требованию при первом блокировке.

Я полагаю, (заметьте, я не знаю, как он на самом деле делает это!), Что он использует атомную сравнить-и-обмена, чтобы связать объект и его syncblk в поточно-образом:

  1. Check скрытое поле syncblk_index нашего объекта для 0. Если это не 0, заблокируйте его и продолжайте, в противном случае ...
  2. Создайте новую синхронизацию в глобальной таблице, получите индекс для нее (глобальные блокировки приобретаются/освобождаются здесь по мере необходимости).
  3. Сравните и обмен, чтобы записать его в объект.
  4. Если предыдущее значение было 0 (предположим, что 0 не является допустимым индексом и является начальным значением для скрытого поля syncblk_index наших объектов), наше создание syncblk не оспаривалось. Заблокируйте его и продолжайте.
  5. Если предыдущее значение не было 0, то кто-то уже создал синхронизацию и связал ее с объектом, пока мы создавали наш, и теперь у нас есть индекс этой syncblk. Уберите тот, который мы только что создали, и заблокируем тот, который мы получили.

Таким образом, накладные расходы на каждый объект составляют 4 байта (при условии 32-разрядных индексов в таблице syncblk) в лучшем случае, но больше для объектов, которые фактически были заблокированы. Если вы редко блокируете свои объекты, то эта схема выглядит как хороший способ сократить использование ресурсов. Но если вам нужно в любой момент заблокировать большинство или все ваши объекты, сохранение мьютекса непосредственно внутри объекта может быть быстрее.

+0

«если вам нужно заблокировать большинство или все ваши объекты в конце концов» ... тогда вы не заслуживаете того, чтобы ваша программа быстро запускалась ;-) –

+0

Я должен был быть более конкретным: конечно, я имел в виду объекты определенного класс, а не все объекты в целом. Часто для объектов определенного класса, несомненно, что в какой-то момент жизни любой экземпляр этого класса должен быть заблокирован. Если это так, выделение мьютекса непосредственно внутри объекта может иметь больше смысла. –

1

Несомненно, вам не нужен такой монитор для каждый объект!

При переносе с Java на C++ это выглядит мне плохой идеей, чтобы просто скопировать все вслепую. Лучшая структура для Java - это не то же самое, что и лучшее для C++, не в последнюю очередь потому, что Java имеет сбор мусора, а C++ - нет.

Добавить монитор только к тем объектам, которые действительно в нем нуждаются. Если только некоторые экземпляры типа нуждаются в синхронизации, не так сложно создать класс-оболочку, который содержит мьютекс (и, возможно, переменную условия), необходимую для синхронизации. Как уже говорили другие, альтернативой является использование пула объектов синхронизации с некоторыми средствами выбора одного для каждого объекта, например, использование хэша адреса объекта для индексации массива.

Я бы использовал библиотеку потокового потока или новую стандартную библиотеку потоков C++ 0x для переносимости, а не полагался на специфику платформы на каждом шагу. Boost.Thread поддерживает Linux, MacOSX, win32, Solaris, HP-UX и другие. Мой implementation of the C++0x thread library в настоящее время поддерживает только ОС Windows и Linux, но другие реализации станут доступны со временем.

Смежные вопросы