Есть ли накладные расходы при преобразовании объектов одного типа в другой? Или компилятор просто все разрешает, и во время выполнения нет затрат?
Это общие вещи, или бывают разные случаи?
Например, предположим, что у нас есть массив Object [], где каждый элемент может иметь другой тип. Но мы всегда точно знаем, что, скажем, элемент 0 - это Double, а элемент 1 - это String. (Я знаю, что это неправильный дизайн, но давайте предположим, что мне пришлось это сделать.)
Сохраняется ли информация о типе Java во время выполнения? Или все забывается после компиляции, и если мы сделаем (Double) elements [0], мы просто будем следовать указателю и интерпретировать эти 8 байтов как двойные, что бы это ни было?
Мне очень непонятно, как создаются типы в Java. Если у вас есть какие-либо рекомендации по книгам или статьям, тоже спасибо.
Ответы:
Есть 2 типа литья:
Неявное приведение типов к более широкому типу, которое выполняется автоматически и не требует дополнительных затрат:
Явное приведение типов при переходе от более широкого типа к более узкому. В этом случае вы должны явно использовать приведение типа:
Во втором случае во время выполнения возникают накладные расходы, поскольку необходимо проверить два типа, и в случае, если приведение типов невозможно, JVM должна выдать исключение ClassCastException.
Взято из JavaWorld: стоимость кастинга
источник
((String)o).someMethodOfCastedClass()
Для разумной реализации Java:
Каждый объект имеет заголовок, содержащий, помимо прочего, указатель на тип среды выполнения (например,
Double
илиString
, но никогда не может бытьCharSequence
илиAbstractList
). Предполагая, что компилятор времени выполнения (обычно HotSpot в случае Sun) не может определить тип статически, необходимо выполнить некоторую проверку с помощью сгенерированного машинного кода.Сначала необходимо прочитать указатель на тип среды выполнения. В любом случае это необходимо для вызова виртуального метода в аналогичной ситуации.
Для приведения к типу класса точно известно, сколько суперклассов существует до тех пор, пока вы не нажмете
java.lang.Object
, поэтому тип может быть прочитан с постоянным смещением от указателя типа (фактически, первые восемь в HotSpot). Опять же, это аналогично чтению указателя на виртуальный метод.Затем считываемому значению просто нужно сравнение с ожидаемым статическим типом приведения. В зависимости от архитектуры набора команд, другая инструкция должна будет перейти (или дать сбой) в неправильном ответвлении. ISA, такие как 32-битная ARM, имеют условную инструкцию и могут передавать печальный путь через счастливый путь.
Интерфейсы сложнее из-за множественного наследования интерфейса. Обычно последние два приведения к интерфейсам кэшируются в типе времени выполнения. В самом начале (более десяти лет назад) интерфейсы были немного медленными, но это уже не актуально.
Надеюсь, вы понимаете, что подобные вещи в значительной степени не имеют отношения к производительности. Ваш исходный код важнее. С точки зрения производительности, самым большим ударом в вашем сценарии могут быть промахи кеша из-за повсеместного преследования указателей объектов (информация о типе, конечно, будет общей).
источник
Компилятор не замечает типы отдельных элементов массива. Он просто проверяет, можно ли присвоить тип каждого выражения элемента типу элемента массива.
Некоторая информация сохраняется во время выполнения, но не статические типы отдельных элементов. Вы можете сказать это, посмотрев на формат файла класса.
Теоретически возможно, что JIT-компилятор может использовать «escape-анализ» для устранения ненужных проверок типов в некоторых присваиваниях. Однако выполнение этого в предлагаемой вами степени будет выходить за рамки реалистичной оптимизации. Эффект от анализа типов отдельных элементов был бы слишком мал.
Кроме того, люди все равно не должны так писать код приложения.
источник
(float) Math.toDegrees(theta)
Будут ли здесь значительные накладные расходы?Вызывается инструкция байт-кода для выполнения приведения во время выполнения
checkcast
. Вы можете дизассемблировать код Java, используя,javap
чтобы увидеть, какие инструкции генерируются.Для массивов Java сохраняет информацию о типе во время выполнения. В большинстве случаев компилятор выявляет ошибки типа для вас, но бывают случаи, когда вы столкнетесь с ошибкой
ArrayStoreException
при попытке сохранить объект в массиве, но тип не совпадает (и компилятор не уловил его) . Спецификация языка Java дает следующий пример:Point[] pa = cpa
допустимо, посколькуColoredPoint
является подклассом Point, ноpa[0] = new Point()
недействительно.Это противоположно универсальным типам, где информация о типе не хранится во время выполнения. При необходимости компилятор вставляет
checkcast
инструкции.Эта разница в типировании для универсальных типов и массивов часто делает непригодным для смешивания массивов и универсальных типов.
источник
Теоретически здесь вводятся накладные расходы. Однако современные JVM умны. Каждая реализация отличается, но есть основания полагать, что может существовать реализация, оптимизированная JIT для исключения проверок приведения, когда она может гарантировать, что никогда не будет конфликта. Что касается того, какие именно JVM предлагают это, я не могу вам сказать. Я должен признать, что хотел бы сам знать особенности JIT-оптимизации, но это для инженеров JVM, о которых нужно беспокоиться.
Мораль этой истории - сначала написать понятный код. Если вы столкнулись с замедлением, профилируйте и определите свою проблему. Велика вероятность, что это не из-за кастинга. Никогда не жертвуйте чистым и безопасным кодом, пытаясь его оптимизировать, ДО тех пор, пока ВЫ НЕ ЗНАЕТЕ, ЧТО ВАМ НУЖНО.
источник