Возможны ли массивы структур в Java? - PullRequest
1 голос
/ 22 февраля 2012

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

Пример: у вас есть class Point { float x; float y;}. Теперь вы хотите сохранить N точек в массиве, который бы занимал как минимум N * 8 байтов для чисел с плавающей запятой и N * 4 байта для ссылки на 32-битную JVM. Так что, по крайней мере, 1/3 - это мусор (не считая обычных издержек объекта). Но если вы сохраните это в двух массивах с плавающей точкой, все будет в порядке.

Мой вопрос : Почему Java не оптимизирует использование памяти для массивов ссылок? Я имею в виду, почему бы не встроить объект непосредственно в массив, как это делается в C ++?

например. маркировка класса Point final должна быть достаточной для того, чтобы JVM увидела максимальную длину данных для класса Point. Или где это будет против спецификации ? Также это сэкономит много памяти при обработке больших n-мерных матриц и т. Д.

Обновление :

Я хотел бы знать, может ли JVM теоретически оптимизировать его (например, за кулисами) и в каких условиях - не более ли я могу каким-либо образом форсировать JVM. Я думаю, что второй пункт этого заключения - причина, по которой это нельзя сделать легко, если вообще.

Выводы, что JVM должна знать:

  1. Класс должен быть последним, чтобы позволить JVM угадать длину одной записи массива
  2. Массив должен быть только для чтения. Конечно, вы можете изменить значения как Point p = arr[i]; p.setX(i), но вы не можете записать в массив через inlineArr[i] = new Point(). Или JVM должна была бы ввести семантику копирования, которая противоречила бы «пути Java». См. Ответ Арофа
  3. Как инициализировать массив (вызывая конструктор по умолчанию или оставляя элементы инициализированными до их значений по умолчанию)

Ответы [ 3 ]

3 голосов
/ 22 февраля 2012

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

ВJava, вы нацелены на JVM.JVM не определяет, допустимо ли это (я делаю предположение, что это правда; я не тщательно прочесал JLS, чтобы доказать, что я здесь).Идея состоит в том, что когда вы пишете код Java, вы доверяете JIT принимать разумные решения.Вот где ссылочные типы могут быть свернуты в массив или тому подобное.Таким образом, «способ Java» здесь заключается в том, что вы не можете указать, происходит ли это или нет, но если JIT может выполнить эту оптимизацию и повысить производительность, она может и должна.

Я не уверен, является ли эта оптимизация в частностиреализован, но я знаю, что подобные: например, объекты, выделенные с помощью new, концептуально находятся в «куче», но если JVM замечает (с помощью метода, называемого escape-анализом), что объект является локальным для метода, то этоможет выделять поля объекта в стеке или даже непосредственно в регистрах ЦП, полностью исключая издержки «выделения кучи» без изменения языка.

Обновление для обновленного вопроса

Если вопрос «можно ли вообще это сделать», я думаю, что ответ - да.Есть несколько угловых случаев (таких как нулевые указатели), но вы должны быть в состоянии обойти их.Для нулевых ссылок JVM может убедить себя в том, что никогда не будет пустых элементов, или сохранить битовый вектор, как упоминалось ранее.Оба эти метода, скорее всего, будут основаны на escape-анализе, показывающем, что ссылка на массив никогда не покидает метод, так как я вижу, как бухгалтерия становится сложной, если вы попытаетесь, например, сохранить ее в поле объекта.

1 голос
/ 22 февраля 2012

Сценарий, который вы описываете, может сэкономить память (хотя на практике я не уверен, что он вообще это сделает), но он, вероятно, добавит немало вычислительных затрат при фактическом размещении объекта в массиве.Учтите, что когда вы делаете new Point() объект, который вы создаете, динамически размещается в куче.Таким образом, если вы выделяете 100 Point экземпляров, вызывая new Point(), нет гарантии, что их расположения будут смежными в памяти (и на самом деле они, скорее всего, не будут выделены смежному блоку памяти).

Так как же экземпляр Point может на самом деле попасть в «сжатый» массив?Мне кажется, что Java должен был бы явно скопировать каждое поле в Point в непрерывный блок памяти, выделенный для массива.Это может стать дорогостоящим для типов объектов, которые имеют много полей.Кроме того, оригинальный экземпляр Point все еще занимает место в куче, а также внутри массива.Таким образом, если он не будет сразу же собран сборщиком мусора (я полагаю, что любые ссылки могут быть переписаны так, чтобы указывать на копию, помещенную в массив, тем самым теоретически разрешая немедленную сборку мусора исходного экземпляра), вы фактически используете больше памяти, чем могли быбыть, если вы только что сохранили ссылку в массиве.

Более того, что если у вас есть несколько «сжатых» массивов и тип изменяемого объекта?Вставка объекта в массив обязательно копирует поля этого объекта в массив.Поэтому, если вы сделаете что-то вроде:

Point p = new Point(0, 0);
Point[] compressedA = {p};  //assuming 'p' is "optimally" stored as {0,0}
Point[] compressedB = {p};  //assuming 'p' is "optimally" stored as {0,0}

compressedA[0].setX(5)  
compressedB[0].setX(1)  

System.out.println(p.x);
System.out.println(compressedA[0].x);
System.out.println(compressedB[0].x);

... вы получите:

0
5
1

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

0 голосов
/ 22 февраля 2012

Разве это не равносильно предоставлению тривиальных классов, таких как следующие?

class Fixed {
   float hiddenArr[];
   Point pointArray(int position) {
      return new Point(hiddenArr[position*2], hiddenArr[position*2+1]);
   }
}

Кроме того, это можно реализовать без явного заявления программиста о том, что им это понравится; JVM уже знает о «типах значений» (типы POD в C ++); те, в которых есть только другие простые старые типы данных. Я полагаю, что HotSpot использует эту информацию при отборе стека, нет причины, по которой он не мог бы сделать это и для массивов?

...