Contigious Pages / Физическая память на Java - PullRequest
0 голосов
/ 06 мая 2009

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

Мои вопросы:

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

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

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

Ответы [ 6 ]

6 голосов
/ 06 мая 2009

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

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

Бит фона:

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

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

4 голосов
/ 06 мая 2009

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

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

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

2 голосов
/ 08 мая 2009

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

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

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

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

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

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

2 голосов
/ 06 мая 2009

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

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

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

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

2 голосов
/ 06 мая 2009

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

0 голосов
/ 06 мая 2009

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

...