Вопрос

Моя цель — обеспечить, чтобы массив, выделенный в Java, располагался в непрерывной физической памяти.Проблема, с которой я столкнулся, заключается в том, что страницы, на которых распределен массив, как правило, не являются смежными в физической памяти, если только я не выделяю действительно большой массив.

Мои вопросы:

  • Почему действительно большой массив обеспечивает страницы, которые являются смежными в физической памяти?
  • Есть ли способ обеспечить распределение массива по физической памяти, не требующий увеличения размера массива?
  • Как я могу определить, на какой странице или физическом адресе существует объект/массив Java, без измерения попаданий/промахов кеша?

Я не ищу ответов на вопросы, почему я делаю это в Java.Я понимаю, что C «решит мою проблему», и что я иду против фундаментальной природы Java.Тем не менее у меня есть веская причина сделать это.

Ответы не обязательно должны работать постоянно.Я ищу ответы, которые работают большую часть времени.Дополнительные баллы за креативные, нестандартные ответы, которые ни один разумный Java-программист никогда бы не написал.Это нормально для конкретной платформы (32-разрядная версия x86, 64-разрядная версия).

Это было полезно?

Решение

Учитывая, что сборщик мусора перемещает объекты в (логической) памяти, я думаю, вам не повезет.

Лучшее, что вы можете сделать, это использовать ByteBuffer.allocateDirect.Он (как правило) не перемещается по (логической) памяти сборщиком мусора, но может быть перемещен в физическую память или даже выгружен на диск.Если вам нужны лучшие гарантии, вам придется использовать ОС.

При этом, если вы можете установить размер страницы равным размеру вашей кучи, тогда все массивы обязательно будут физически непрерывными (или заменены местами).

Другие советы

Нет.Физически непрерывная память требует прямого взаимодействия с ОС.Большинство приложений, включая JVM, получают только практически смежные адреса.И JVM не может дать вам то, чего она не получает от ОС.

Кроме того, зачем вам это нужно?Если вы настраиваете передачу DMA, вы, вероятно, все равно используете методы, отличные от Java.

Немного предыстории:

Физическая память в современном ПК обычно представляет собой гибкий объем в сменных модулях DIMM.Каждый его байт имеет физический адрес, поэтому операционная система во время загрузки определяет, какие физические адреса доступны.Оказывается, приложениям выгоднее не использовать эти адреса напрямую.Вместо этого все современные процессоры (и их кэши) используют виртуальные адреса.Существует таблица сопоставления с физическими адресами, но она не обязательно должна быть полной — обмен на диск возможен за счет использования виртуальных адресов, не сопоставленных с физическими адресами.Другой уровень гибкости достигается за счет наличия одной таблицы для каждого процесса с неполными сопоставлениями.Если у процесса A есть виртуальный адрес, который отображается на физический адрес X, а у процесса B его нет, то процесс B не может писать по физическому адресу X, и мы можем считать, что эта память принадлежит исключительно процессу A.Очевидно, что для того, чтобы это было безопасно, ОС должна защищать доступ к таблице сопоставлений, но все современные ОС это делают.

Таблица сопоставления работает на уровне страницы.Страница или непрерывное подмножество физических адресов отображается в непрерывное подмножество виртуальных адресов.Компромисс между накладными расходами и степенью детализации привел к тому, что страницы размером 4 КБ стали обычным размером страницы.Но поскольку каждая страница имеет свое собственное отображение, нельзя предполагать непрерывность за пределами этого размера страницы.В частности, когда страницы удаляются из физической памяти, перемещаются на диск и восстанавливаются, вполне возможно, что они окажутся по новому адресу физической памяти.Программа этого не замечает, так как виртуальный адрес не меняется, меняется только таблица сопоставлений, управляемая ОС.

Я думаю, что вы захотите использовать sun.java.unsafe.

Могут быть способы заставить конкретную JVM делать то, что вы хотите, но они, вероятно, будут хрупкими, сложными и, скорее всего, очень специфичными для JVM, ее версии, ОС, на которой она работает, и т. д.Другими словами, напрасные усилия.

Поэтому, не зная больше о вашей проблеме, я не думаю, что кто-то сможет помочь.На Java вообще это невозможно сделать, максимум на конкретной JVM.

Чтобы предложить альтернативу:

Если вам действительно необходимо хранить данные в непрерывной памяти, почему бы не сделать это в небольшой библиотеке C и не вызвать ее через JNI?

Как я вижу это.Вам еще предстоит объяснить, почему

  • что примитивные массивы не являются непрерывными в памяти.Я не понимаю, почему они не могут быть непрерывными в виртуальной памяти.(см.Массивы объектов вряд ли будут иметь непрерывные объекты в памяти)
  • массив, который не является непрерывным в физической памяти (ОЗУ, т.е.Оперативная память) будет иметь значительную разницу в производительности.напримеризмеримую разницу в производительности вашего приложения.

Похоже, что вы действительно ищете низкоуровневый способ распределения массивов, потому что вы привыкли делать это в C, и производительность является требованием необходимости сделать это.

КСТАТИ:Доступ к ByteBuffer.allocateDirect() с помощью, скажем, getDouble()/putDouble() может быть медленнее, чем просто использование double[], поскольку первый вариант включает в себя вызовы JNI, а второй можно оптимизировать так, чтобы он вообще не вызывался.

Причина, по которой он используется, заключается в обмене данными между пространствами Java и C.напримерЗвонит НИО.Он работает хорошо только тогда, когда чтение/запись сведены к минимуму.В противном случае вам лучше использовать что-нибудь из области Java.

то естьЕсли вам не ясно, что вы делаете, и почему если вы это делаете, вы можете получить решение, которое может улучшить ваше самочувствие, но на самом деле оно более сложное и работает хуже, чем простое решение.

Примечание этот ответ на связанный вопрос, в котором обсуждается System.identityHashCode() и идентификация адреса памяти объекта.Суть в том, что вы можете использовать реализацию hashCode() массива по умолчанию для идентификации исходного адреса памяти массива (при условии размещения в int/32-битном формате).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top