Java — Для чего нужен байт — код в Java


Содержание

Байт-код

Все языки можно условно разделить на компилируемые и интерпретируемые. В Java используется третий подход — байт-код. Исходный код Java преобразуется компилятором в байт-код (а не машинный код). A байт-код Java преобразуется в машинный код с помощью специального интерпретатора, называемого виртуальной машиной Java (Java Virtual Machine — JVM).

Рассмотрим более детально как работает Java:

  1. Создается исходный документ (исходник) – файл c расширением .java.
  2. Исходник пропускается через компилятор, который проверяет код на ошибки и выдает конечный результат.
  3. Компилятор создает новый документ, закодированный с помощью байт-кода. Любое устройство, способное выполнять Java, сможет интерпретировать этот файл в такой формат, который сможет запустить. Скомпилированный байт-код не зависит от платформы.
  4. Виртуальная машина считывает и выполняет байт-код.

Литература: Head First Java, 2nd Edition. Глава 1 — Как работает Java.

Блог только про Java

Учимся программировать на Java с нуля

Чтение и запись байтов Java

Класс InputStream имеет следующий абстрактный метод: abstract int read(). Этот метод считывает один байт и возвращает либо считанный байт, либо -1, если наталкивается на конец потока входных данных. Разработчик конкретного класса потока входных данных может переопределять этот метод так, чтобы он предоставлял какую-то полезную функциональную возможность.

Например, в классе FileInputStream этот метод выполняет считывание одного байта из файла. System.in представляет собой переопределенный объект подкласса InputStream, который позволяет считывать информацию с клавиатуры.

У класса InputStream еще также имеются и неабстрактные методы для считывания массива байтов или для пропускания ряда байтов. Эти методы вызывают абстрактный метод read, благодаря чему подклассам нужно переопределять только один метод. Аналогичным образом, класс OutputStream определяет следующий абстрактный метод, который записывает один байт в место выходных данных:

Как методы read, так и методы write блокируют доступ до тех пор, пока байты действительно не будут считаны или записаны. Это означает, что если к потоку данных не удается получить доступ мгновенно(что обычно происходит из-за занятности сетевого подключения), происходит блокирование текущего потока задач(thread). А это дает другим потокам задач возможность выполнять какую-то полезную работу, пока метод ожидает, когда поток данных снова станет доступным.

Программа для контроля работы торговых представителей http://www.terrasoft.ua/industry/fmcg. Автоматизация, управление трейд-маркетингом и мерчендайзингом.

Метод available позволяет проверять, какое количество байтов доступно для считывания в текущий момент. Это означает, что фрагмент кода, вроде того, что показан ниже, вряд ли будет приводить к блокировке:

По завершении процесса считывания или записи данных в поток нужно обязательно закрывать его путем вызова метода close. Такой вызов проводит к освобождению системных ресурсов, доступных в ограниченном количестве. В случае открытия приложением множества потоков без последующего их закрытия, ресурсы системы могут истощаться.

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

Даже если класс потока предоставляет конкретные методы для работы с исходными функциями read и write, программисты приложений редко их используют. Для них интерес чаще представляют все-таки данные, содержащие числа, строки и объекты, а не исходные байты.

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

Нужно ли составлять Java для байт-кода? — java

Указывает ли спецификация языка Java, что Java скомпилирован в байт-код Java?

Из того, что я понимаю, это не так:

Время компиляции обычноперевода программ в машинно-независимый байт-код [представление.

Язык программирования Java обычно, скомпилированный для команды байт-кода set и двоичный формат, определенный в Спецификации виртуальной машины Java, Java SE 9.

Я не могу найти никаких других упоминаний «байтового кода» или «байт-кода» в спецификации.

Означает ли это, что все манипуляции с байт-кодом технически не покрываются «Java-языком», как определено JLS, и технически полагаются на детали реализации?

    2 2
  • 19 окт 2020 2020-10-19 20:51:54
  • phant0m

2 ответа

JSL не должен знать, как он будет читаться JVM, он описывает только язык Java. Компилятор (JAVAC), предоставленный в JDK, делает ссылку, но не является частью самого языка

Программное обеспечение Oracle JDK содержит компилятор из исходного кода, написанного на языке программирования Java, в набор команд виртуальной машины Java

Компилятор языка программирования Java, javac, читает исходные файлы, написанные на языке программирования Java, и компилирует их в файлы классов байткода. При желании компилятор также может обрабатывать аннотации, найденные в исходных и классных файлах, с помощью API Pluging Annotation Processing. Компилятор является инструментом командной строки, но также может быть вызван с использованием Java Compiler API. Компилятор принимает исходный код, определенный спецификацией Java Language Specification (JLS), и создает файлы классов, определенные спецификацией виртуальной машины Java (JVMS).

Таким образом, команда JAVAC в основном является мостом между спецификациями.


  • Вход: a.java описать в JLS
  • Выход: a.class descrive в JVMS.

Вы можете найти некоторую информацию, проверив Jave Virtual Machine Specification.

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

(Я хотел бы найти противоположное утверждение, что язык Java ничего не знает о языке виртуальной машины Java. )

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

  • 19 окт 2020 2020-10-19 20:51:55
  • AxelH

Вы заметили, что термин «обычно», а также отсутствие описания байтового кода в JLS предназначены для того, чтобы определить язык программирования Java как независимый от среды исполнения, насколько это возможно. Тем не менее, это не так просто:

Связь с предопределенными классами и интерфейсами

Как отмечалось выше, эта спецификация часто относится к классам API платформы Java SE. В частности, некоторые классы имеют особые отношения с языком программирования Java. Примеры включают классы, такие как Object , Class , ClassLoader , String , Thread , а классы и интерфейсы в пакете java.lang.reflect , среди прочих. Эта спецификация ограничивает поведение таких классов и интерфейсов, но не дает полной спецификации для них. Читатель ссылается на документацию API платформы Java SE.

Следовательно, эта спецификация не описывает отражение в деталях. Многие лингвистические конструкции имеют аналоги в API Core Reflection ( java.lang.reflect ) и API-интерфейсе языковых моделей ( javax.lang.model ), но они обычно не обсуждаются здесь. Например, когда мы перечисляем способы создания объекта, мы обычно не включаем способы, с помощью которых API Core Reflection может выполнить это. Читатели должны знать об этих дополнительных механизмах, даже если они не упоминаются в тексте.

Таким образом, язык программирования Java больше, чем JLS, а также API платформы Java SE. И там, у нас есть методы defineClass упомянутого класса ClassLoader , принимающие входные данные в формате файла класса. Поэтому, даже если мы используем другие способы развертывания, чем файлы классов в формате байт-кода, полностью совместимая среда должна поддерживать этот формат в этом месте. Обратите внимание, что Java 9 ввела другой метод, принимающий ввод в формате файла класса, который даже не требует Reflection или реализации пользовательских загрузчиков классов.

Это исключает JavaME, который не имеет этих артефактов API, упомянутых JLS, иначе у нас уже был пример среды Java, не поддерживающей манипуляции байт-кодом.

Но это все еще не полностью отвечает на вопрос, не работает ли манипуляция байт-кода вне языка, говоря о JavaSE или EE. Даже если поддержка формата байт-кода обеспечивается стандартным API, управление байт-кодом зависит от деталей реализации, либо API-интерфейс Instrumentation, поддержка которого не является обязательной, либо обрабатывает скомпилированные файлы классов в их развернутой форме, как файловая иерархия, файлы jar или модуль файлов, и не гарантируется, что это будет развернутая форма приложения (как сказано в начале). Таким образом, на самом деле невозможно реализовать инструмент манипуляции байт-кодами, который гарантированно работает со всеми возможными средами Java, хотя вам нужно будет сделать большие длины для создания среды полностью совместим, но не работает с этими инструментами.

Почему для языка Java нужен байт-код? Почему дизайн Java таким образом?

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

Это ради скорости и переносимости в одно и то же время.

Все, что я собираюсь сказать, нужно адаптировать и модерировать в зависимости от случая, но грубо:

Если вы просто интерпретируете java файл с помощью интерпретатора, вы будет иметь переносимость, но не скорость .

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

С байт-кодом вы компилируете код (в байт-код) для общего машина, которая ее выполнит (JVM), это компромисс между скорость и переносимость .

Почему бы не просто использовать язык сценариев, используя интерпретатор, чтобы интерпретатор .java файл? или почему бы просто не преобразовать его в исполняемый файл как C? Зачем нужно преобразовывать в байт-код?

Я думаю, что намерение состояло в том, чтобы

1) Безопасность времени компиляции 2) Напишите один раз, бегите в любом месте.

Если вы конвертируете в исполняемый файл, например C, вы потеряете # 2. В некотором смысле, JVM является интерпретатором, поэтому байт-код Java интерпретируется, а код кода Java скомпилирован.

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

или почему бы просто не преобразовать его в исполняемый файл, например C?

Потому что это не будет работать кросс-платформенно. A Portable Executable (используется в Windows) не будет работать, скажем, в Linux или iOS, по крайней мере, не без трюков.

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

Почему Java-дизайн таким образом?

Пишите один раз, бегите всюду — переносимость.

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

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


или почему бы просто не преобразовать его в исполняемый файл, например C?

Поскольку разные платформы требуют разных исполняемых двоичных файлов. Байт-код Java (снова) переносится.

Каково использование преобразования исходного кода в байт-код Java?

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

Цукерберг рекомендует:  Программировавние - На какой ОС лучше начать программировать

8 ответов

Логика заключается в том, что байт-код JVM намного проще, чем исходный код Java.

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

Анализ состоит из чтения кода и превращения его в древовидное представление внутри памяти компилятора. Семантический анализ — это часть, где он анализирует это дерево, определяет, что это означает, и упрощает все конструкции высокого уровня до более низких уровней. И генерация кода берет упрощенное дерево и записывает его в плоский выход.

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

Это делает задачу, которую JIT выполняет намного проще и, следовательно, намного быстрее выполнять, сохраняя при этом метаданные высокого уровня и семантическую информацию, что позволяет теоретически писать одностраничный кросс-платформенный код .

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

В случае Java причина номер один изначально была, вероятно, переносимостью . Java была первоначально продана вначале как «Write Once, Run Anywhere». Хотя вы можете добиться этого, распределив исходный код и используя разные компиляторы для ориентации на разные платформы, это имеет несколько недостатков:

  • компиляторы — это сложные инструменты, которые должны понимать все удобные синтаксисы языка; байт-код может быть более простым языком, поскольку он ближе к машиноисполняемому коду, чем читаемый человеком источник; это означает:
    • компиляция может быть медленной по сравнению с исполнением байт-кода Компиляторы
    • , предназначенные для разных платформ, могут привести к различным поведением или не поддерживать языковые изменения.
    • создание компилятора для новой платформы намного сложнее, чем создание VM (или байт-кода для собственного компилятора) для этой платформы
  • распространение исходного кода не всегда желательно; байт-код предлагает некоторую защиту от обратной инженерии (хотя ее довольно легко декомпилировать, если умышленно не запутаться).

Другие преимущества промежуточного представления включают:

  • Оптимизация , где шаблоны могут быть замечены в байт-коде и скомпилированы до более быстрых эквивалентов или даже оптимизированы для особых случаев по мере запуска программы (с использованием компилятора «JIT» или «Just In Time»)
  • интероперабельность между несколькими языками в одной виртуальной машине; это стало популярным в JVM (например, Scala) и является явной целью инфраструктуры .net

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

Ясно, что ответ здесь заключается в том, что Java по дизайну не предполагает, что он знает, на какой машине будет работать ваш код; это может быть рабочий стол, суперкомпьютер, телефон или что-то еще между ними. Java оставляет место для локального компилятора JVM для выполнения своей задачи. В дополнение к увеличению переносимости вашего кода, это имеет приятное преимущество, позволяя компилятору делать такие вещи, как использовать оптимизацию, зависящую от машины, если они существуют или по-прежнему воспроизводить хотя бы рабочий код, если они этого не делают. Такие вещи, как инструкции SSE или аппаратное ускорение, могут использоваться только на тех машинах, которые их поддерживают.

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

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

Обратите внимание, что я не упоминаю более быстрое выполнение. Как исходный код, так и байтовый код могут или могут (теоретически) быть полностью скомпилированы для одного и того же машинного кода для фактического выполнения.

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

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

Компиляторы вообще

Давайте сначала рассмотрим общий вопрос: почему компилятор использовал некоторое промежуточное представление в процессе компиляции исходного кода для работы на каком-то конкретном процессоре?

Снижение сложности

Один ответ на этот вопрос довольно прост: он преобразует проблему O (N * M) в проблему O (N + M).

Если нам даны N исходных языков и M-мишеней, и каждый компилятор полностью независим, нам нужны компиляторы N * M для перевода всех этих исходных языков ко всем этим целям (где «цель» — это что-то вроде комбинация процессора и ОС).

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

Сегментация проблем

Еще лучше, он отделяет проблему от двух более или менее эксклюзивных доменов. Люди, которые знают /заботятся о дизайне языка, разборе и т. Д., Могут сосредоточиться на концах компилятора, в то время как люди, которые знают о наборах инструкций, дизайне процессора и т. Д., Могут сосредоточиться на задней части.


Так, например, учитывая что-то вроде LLVM, у нас много интерфейсов для разных языков. У нас также есть серверы для множества различных процессоров. Парень языка может написать новый интерфейс для своего языка и быстро поддерживать множество целей. Парень процессора может написать новый back-end для своей цели, не имея дело с языковым дизайном, разбором и т. Д.

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

Модели распределения

В той мере, в которой Java добавила что-то новое в этом отношении, оно было в модели распределения. В частности, несмотря на то, что компиляторы были разделены на внешние и внешние элементы внутри компании в течение длительного времени, они обычно распределялись как один продукт. Например, если вы купили компилятор Microsoft C, внутри него были «C1» и «C2», которые были соответственно интерфейсом и back-end — но вы купили только «Microsoft C», который включал оба штук (с «драйвером компилятора», который координировал операции между ними). Несмотря на то, что компилятор был построен двумя частями, для обычного разработчика, использующего компилятор, это была всего лишь одна вещь, которая переводилась с исходного кода на объектный, без видимых промежутков между ними.

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

Прецеденты

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

Байт-код Java

Java-байтовый код очень похож на P-код. Это в основном инструкции для простой машины справедливо . Эта машина предназначена для абстракции существующих машин, поэтому ее довольно легко перевести практически на любую конкретную цель. Простота перевода была важна на раннем этапе, поскольку первоначальным намерением было интерпретировать байткоды, как и P-System (и, да, именно так работали ранние реализации).

Сильные стороны

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

Байт-коды Java довольно компактны — в большинстве случаев гораздо компактнее, чем исходный код или машинный код для большинства типичных процессоров (и особенно для большинства RISC-процессоров, таких как SPARC, которые Sun продает при разработке Java ). Это было особенно важно в то время, потому что одним из основных намерений Java было поддерживать апплеты — код, встроенный в веб-страницы, которые будут загружены перед исполнением — в то время, когда большинство людей обращались к нам через модемы по телефонным линиям примерно на 28,8 килобит в секунду (хотя, конечно, все еще было немало людей, использующих более старые, более медленные модемы).

Слабые стороны

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

Например, довольно обычная процедура: если вы действительно хотите оптимизировать байт-коды Java, вы в основном делаете некоторые обратные инжиниринги, чтобы перевести их назад из машинного кода, например представления, и вернуть их обратно в инструкции SSA (или что-то подобное) 2 . Затем вы управляете инструкциями SSA для оптимизации, а затем переводите оттуда к чему-то, что ориентируется на архитектуру, которая вам действительно нужна. Однако даже при таком довольно сложном процессе некоторые концепции, не относящиеся к Java, достаточно сложно выразить, что трудно перевести с некоторых исходных языков на машинный код, который работает (даже близко к) оптимально на большинстве типичных машин.

Резюме

Если вы спрашиваете, зачем использовать промежуточные представления в целом, два основных фактора:

  1. Уменьшить проблему O (N * M) до проблемы O (N + M) и
  2. Разделите проблему на более управляемые части.

Если вы спрашиваете о специфике байт-кодов Java и почему они выбрали это конкретное представление вместо какого-то другого, я бы сказал, что ответ в основном возвращается к их первоначальному намерению и ограничениям в Интернете в то время, что привело к следующим приоритетам:

  1. Компактное представление.
  2. Быстрое и простое декодирование и выполнение.
  3. Быстрая и простая реализация на большинстве обычных машин.

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

  1. Итак, почему P-система в основном забыта? В основном ситуация с ценой. P-система продавалась довольно прилично на Apple II, Commodore SuperPets и т. Д. Когда вышел IBM PC, P-система была поддерживаемой ОС, но MS-DOS стоила меньше (с точки зрения большинства людей, по сути была брошена бесплатно) и быстро было доступно больше программ, так как это пишут Microsoft и IBM (среди прочих).
  2. Например, так работает Сажа .

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

Это также упрощает защиту исходного кода, защищенного авторскими правами.

Смысл заключается в том, что компиляция из байтового кода в машинный код выполняется быстрее, чем интерпретация исходного кода на машинный код как раз вовремя. Но нам нужны интерпретации, чтобы сделать наше приложение кросс-платформенным, потому что мы хотим использовать наш оригинальный код на каждой платформе без изменений и без каких-либо подготовки (компиляции). Таким образом, сначала javac компилирует наш исходный код в байтовый код, тогда мы можем запускать этот байтовый код в любом месте, и он будет быстрее интерпретироваться с помощью виртуальной машины Java для машинного кода. Ответ: это экономит время.

Цукерберг рекомендует:  Английский язык - Английский язык в IT

Первоначально JVM был чистым интерпретатором . И вы получаете наилучшего переводчика, если язык, который вы интерпретируете, как simple , насколько это возможно. Это было целью байтового кода: обеспечить эффективный интерпретируемый ввод среды выполнения. Это единственное решение поставило Java ближе к скомпилированному языку, чем к интерпретируемому языку, о чем свидетельствует его производительность.

Только позже, когда стало очевидно, что производительность интерпретирующих JVM все еще сосала, люди вкладывали усилия для создания хорошо исполняемых компиляторов «точно в срок». Это несколько закрыло пробел для более быстрых языков, таких как C и C ++. (Некоторые проблемы со встроенной скоростью Java остаются, однако, поэтому вы, вероятно, никогда не получите среду Java, которая выполняет так же хорошо написанный код C.)

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

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

Байт-код — это структура, которая намеревается быть легкой для прочитанной и выполненной машиной.

Поскольку все JVM с кодом считывается и выполняет его, байт-код лучше подходит для использования JVM.

Я заметил, что пока еще не было примеров. Примеры глупых псевдо:

Конечно, байтовый код — это не просто оптимизация. Большая его часть заключается в возможности выполнять код, не заботясь о сложных правилах, например, проверяя, содержит ли класс член, называемый «foo», где-то дальше в файле, когда метод ссылается на «foo».


Почему нельзя единожды скомпилировать байт-код на конечной машине

Что делает jvm? Учитывая все особенности среды в которой она работает (так как она именно под нее написана), интерпретирует байт-код, то есть динамически превращает в машинный код.

Так вот, почему нельзя единожды, при «установке» программы (то есть первом попадании) на компьютер с помощью jvm скомпилировать байт-код в машинный код и дальше уже не использовать jvm? Ускорение ведь будет серьезное и может чуток памяти высвободится за счет отсутствия jvm. Ведь есть же JIT который делает то же самое, но во время выполнения программы и лишь кусками.

4 ответа 4

Так вот, почему нельзя единожды, при «установке» программы (то есть первом попадании) на компьютер с помощью jvm скомпилировать байт-код в машинный код и дальше уже не использовать jvm?

Потому что тогда нельзя будет использовать спекуляции, за счет которых JIT-скомпилированный код вполне легально может обгонять AOT-скомпилированный код. Например, у нас может быть метод со следующей сигнатурой:

В этом случае АОТ ничего не останется кроме того, чтобы использовать интерфейс Consumer и вычислять реально вызываемый метод в рантайме. Однако если JIT на момент компиляции видит, что метод был вызван пять тысяч раз с одной и той же имплементацией Consumer — он может отбросить все лишнее, вызывать напрямую заранее известный метод и поставить перед этим т.н. trap на случай, если в метод все-таки упадет другая имплементация, и его надо будет перекомпилировать.

Также JIT позволяет эффективный инлайнинг кода, который также происходит на основе учета количества вызовов и размера метода. АОТ не может знать, какой участок будет горячим без помощи программиста (который, в свою очередь, может ошибаться), а JIT — вполне.

Ведь есть же JIT который делает то же самое, но во время выполнения программы и лишь кусками.

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

Небольшое не очень профессиональное объяснение работы исполнителей языка.

  1. Компиляторы.
  2. Интерпретаторы.
  3. И компилятора интерпретаторы, или интерпретаторы компилирующего Типа.

Отбросим первые два, и обсудим третий.

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

Соответственно теперь вы сами должны понять, почему нельзя добиться единоразового исполнения — программа на половину, исполняется динамически, то есть, если байт код мы в принципе можем единожды скомпилировать, но интерпретировать байт-код нужно каждый раз уникально. Такая технология называется JIT интерпретаторы.

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

Вопрос по jvm, cross-platform, java &#8211 Независимость от платформы в байт-коде Java

Я иногда задаюсь вопросом, почему Java упоминается как независимый от платформы язык?

Я не мог найти правильное объяснение нижеприведенных пунктов:

Является ли JVM одинаковой для Windows / Linux / Mac OS?Байт-код генерируется одинаково для одного и того же класса в указанных выше средах?

Если ответ на вышеуказанные вопросы НЕТ, то как достигается независимость платформы.

Пожалуйста, помогите мне в изучении этой основной концепции.

Не за что. Компилятор одинаков для всех платформ. Но, так как этозапускаемый файлсам файл будет другим, то есть в Windows, это будет .exe, в Linux, это будет исполняемый файл Linux и т. д.

Байт-код генерируется одинаково для одного и того же класса в указанных выше средах?

Да. Вот почему JavaСОВЕРШЕННО ОДИН РАЗ. Бежать в любом месте.

Компилятор Java (javac) скомпилирует исходный код в байт-код (хранится в файле .class)

Байт-код Java (.class) не зависит от ОС, он имеет одинаковое расширение во всех разных ОС. Но так как это не является специфичным для любой ОС или другой среды, никто не может запустить это (если нет машины, чей собственный набор команд является байт-кодами, то есть они могут понимать сам байт-код)

JVM загрузить и выполнить байт-код

Виртуальная машина (ВМ) является программной реализацией машины (то есть компьютера), которая выполняет программы подобно физической машине. Java также имеет виртуальную машину под названием Java Virtual Machine (JVM).

JVM имеет загрузчик классов, который загружает скомпилированный байт-код Java в области данных времени выполнения. И у него есть механизм выполнения, который выполняет байт-код Java. И что важно, он JVM зависит от платформы. У вас будет другая JVM для разных операционных систем и других сред.

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

Переводчик: Читает, интерпретирует и выполняет инструкции байт-кода одну за другой

JIT (Just-In-Time) компилятор: Компилятор JIT был введен, чтобы компенсировать недостатки интерпретатора. Механизм выполнения сначала запускается как интерпретатор, и в соответствующее время JIT-компилятор компилирует весь байт-код, чтобы изменить его на собственный код. После этого механизм выполнения больше не интерпретирует метод, а непосредственно выполняет с использованием собственного кода. Выполнение в нативном коде намного быстрее, чем интерпретация инструкций одна за другой. Скомпилированный код может быть выполнен быстро, так как собственный код хранится в кэше.

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

Java — Для чего нужен байт — код в Java?


Байт-код — или байткод (англ. byte code), иногда также используется термин псевдокод машинно независимый код низкого уровня, генерируемый транслятором и исполняемый интерпретатором. Большинство инструкций байт кода эквивалентны одной или… … Википедия

байт-код — Машинно независимый код, генерируемый Java компилятором. [ГОСТ Р 54456 2011] Тематики телевидение, радиовещание, видео EN byte codeJava byte code … Справочник технического переводчика

Java Native Interface — (JNI) стандартный механизм для запуска кода, под управлением виртуальной машины Java (JVM), который написан на языках С/С++ или Ассемблера, и скомпонован в виде динамических библиотек, позволяет не использовать статическое связывание. Это… … Википедия

Java Virtual Machine — В этой статье не хватает ссылок на источники информации. Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена. Вы можете … Википедия

Java-апплет — Необходимо проверить качество перевода и привести статью в соответствие со стилистическими правилами Википедии. Вы можете помочь улучшить эту статью, исправив в ней ошибки. Оригинал н … Википедия

Java — разработанная компанией Sun Microsystems система программирования, содержащая: универсальный язык программирования Java; компилятор; и виртуальную машину Java s Virtual Machine (JVM). Компилятор транслирует исходный Java текст в байт код, который … Финансовый словарь

Java — Иное название этого понятия «Ява»; см. также другие значения. Не следует путать с JavaScript. Java Класс языка … Википедия

Java (программная платформа) — Не следует путать с JavaScript. Программная платформа Java ряд программных продуктов и спецификаций компании Sun Microsystems, ранее независимой компании, а ныне дочерней компании корпорации Oracle, которые совместно предоставляют систему для… … Википедия

Код операции — Эта статья об инструкциях; о системе команд в целом см.: Машинный код. Код операции, операционный код, опкод часть машинного языка, называемая инструкцией и определяющая операцию, которая должна быть выполнена. Определение и формат кодов… … Википедия

Код операции (информатика) — Эта статья об инструкциях; о системе команд в целом см.: Машинный код. В комьютерной отрасли под кодом операции (также операционный код, опкод англ. operation code) понимают часть машинного языка, называемую инструкцией, определяющую операцию,… … Википедия

Почему для языка Java нужен байт-код? Почему дизайн Java таким образом?

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

Это ради скорости и переносимости в одно и то же время.

Все, что я собираюсь сказать, нужно адаптировать и модерировать в зависимости от случая, но грубо:

Если вы просто интерпретируете java файл с помощью интерпретатора, вы будет иметь переносимость, но не скорость .

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

С байт-кодом вы компилируете код (в байт-код) для общего машина, которая ее выполнит (JVM), это компромисс между скорость и переносимость .

Почему бы не просто использовать язык сценариев, используя интерпретатор, чтобы интерпретатор .java файл? или почему бы просто не преобразовать его в исполняемый файл как C? Зачем нужно преобразовывать в байт-код?

Я думаю, что намерение состояло в том, чтобы

1) Безопасность времени компиляции 2) Напишите один раз, бегите в любом месте.

Цукерберг рекомендует:  Бесконечное слайдшоу на CSS3

Если вы конвертируете в исполняемый файл, например C, вы потеряете # 2. В некотором смысле, JVM является интерпретатором, поэтому байт-код Java интерпретируется, а код кода Java скомпилирован.

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

или почему бы просто не преобразовать его в исполняемый файл, например C?

Потому что это не будет работать кросс-платформенно. A Portable Executable (используется в Windows) не будет работать, скажем, в Linux или iOS, по крайней мере, не без трюков.

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

Почему Java-дизайн таким образом?

Пишите один раз, бегите всюду — переносимость.

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

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

или почему бы просто не преобразовать его в исполняемый файл, например C?

Поскольку разные платформы требуют разных исполняемых двоичных файлов. Байт-код Java (снова) переносится.

Язык программирования Java: с чего начать изучение. Где применяется Java

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

Основы программирования на языке Java

Java как язык с поддержкой объектного ориентирования отвечает основным принципам ООП:

В центре «Джава», как и в других ООЯ, — объект и класс с конструкторами и свойствами. Начинать обучение языку программирования Java лучше не с официальных ресурсов, а с пособий для новичков. В таких манулах подробно описываются возможности, предоставляются примеры кода. В книгах наподобие «Язык программирования Java для начинающих” подробно разъясняются основные принципы и особенности названного языка.


Особенности

Код на языке программирования Java транслируется в байт-код, затем выполняется на виртуальной машине JVM. Преобразование в байт-код осуществляется на Javac, Jikes, Espresso, GCJ. Существуют такие компиляторы, которые транслируют язык «Си» в Java байт-код. Таким образом, приложение на «Си» может работать на любых платформах.

Синтаксис «Джава» характеризуется следующим:

  1. Имена классов должны начинаться с большой буквы. Если название состоит из нескольких слов, то второе должно начинаться с верхнего регистра.
  2. Если для формирования метода используется несколько слов, то второе из них должно начинаться с большой буквы.
  3. Обработка начинается с метода main() — он является частью каждой программы.

Язык программирования Java имеет 8 примитивных типов. Они представлены ниже.

  • Boolean — логический тип, принимает всего два значения true и false.
  • Byte — наименьший целочисленный тип размером 1 байт. Он используются при работе с потоком данных или файлов, необработанными двоичными данными. Имеет диапазон от -128 до 127.
  • Short имеет диапазон от -32768 до 32767, используется для представления чисел. Размер переменных этого типа — 2 байта.
  • Int тоже обозначает числа, но его размер — 4 байта. Он чаще остальных используется для работы с целочисленными данными, а byte и short иногда повышаются до int.
  • Long используются для больших целых чисел. Возможные значения находятся в диапазоне от -9223372036854775808 до 9223372036854775807.
  • Float и double применяются для обозначения дробных. Их разница в том, что float удобен, когда не требуется высокая точность в дробной части числа.
  • Double выводит на экран все знаки после разделителя «.», а float — только первые.
  • String наиболее используемый примитивный тип, с помощью которого задаются строки.

Классы и объекты

Важную роль в книге «Изучение языка программирования Java для начинающих» занимают классы и объекты.

Класс определяет шаблон для объекта, у него обязательно есть атрибуты и методы. Для его создания применяют ключевое слово Class. Если он создается в отдельном файле, то имя класса и файла должны быть одинаковыми. Само же название состоит из двух частей: имени и расширения .Java.

В «Джава» можно создавать подкласс, который будет наследовать методы родительского. Для этого используется слово extends:

  • class имя_класса extends имя_суперкласса <>;

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

  • public class Class < public Class()< >public Class(String name)< >>

Имя конструктора совпадает с именем класса, по умолчанию у него есть всего один параметр:

Object создается из класса с помощью оператора new():

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

Point р = new Point()

public static void main(String args[]) <

Point p1 = new Point();

Point p2 = new Point();

Объектные переменные и объекты — совершенно разные сущности. Object variables являются ссылками. Они могут указывать на любые переменные непримитивного типа. В отличие от C++ их типовое преобразование жестко регламентировано.

Поля и методы

Поля — это все переменные, связанные с классом или объектом. По умолчанию они являются локальными и не могут использовать в других классах. Для доступа к полям используется оператор «.»:

Можно задать статические поля с помощью ключевого слова static. Такие поля являются единственным способом хранить глобальные переменные. Это связано с тем, что в «Джава» попросту нет global variables.


Реализована возможность импорта переменных для получения доступа из других пакетов:

Метод — подпрограмма для тех классов, в которых объявлена. Описывается на том же уровне, что и переменные. Задается в виде функции и может иметь любой тип, в том числе void:

void init(int a, int b) <

В примере выше класс Point имеет поля типа integer x и y, метод init(). Доступ к методам, как и к переменным, осуществляется путем использования оператора «.»:

Свойство init ничего не возвращает, поэтому имеет тип void.

Переменные

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

Одновременно может быть объявлено несколько переменных. Для их перечисления используется запятая:

Инициализации осуществляется после или во время объявления:

int a = 10, b = 10;

Существует несколько типов:

  • local variables (локальные);
  • instance variables (переменные экземпляра);
  • static variables (статические).

Local variables объявляют в методах и конструкторах, они создаются во время запуска последних и уничтожаются после завершения. Для них запрещено указывать access modifiers и управлять уровнем доступности. Они не видимы за пределами объявленного блока. В Java переменные не имеют начального значения, поэтому оно в обязательном порядке присваивается перед первым использованием.

Instance variables должны быть объявлены внутри класса. Они используются как методы, но получить к ним доступ можно только после создания объекта. Переменная разрушается, когда уничтожается объект. У экземплярных переменных, в отличе от локальных, существуют значения по умолчанию:

Static variables называются переменными класса. Их имена начинаются с символа в верхнем регистре, конкретизируются модификатором static. Они используются как константы, соответственно, к ним прибавляется один спецификатор из списка:

Запускаются в начале программы, уничтожаются после остановки выполнения. Так же, как переменные экземпляра, имеют стандартные значения, которые присваиваются пустым переменным. У чисел — значение 0, булевые переменные имеют значение false, ссылки на объект изначально имеют null. Статические переменные вызываются в следующем виде:

Сборщик мусора

В самоучителе «Язык программирования Java для новичков» раздел автоматического сборщика мусора является наиболее интересным.

В Java, в отличие от языка «Си», невозможно ручное удаление объекта из памяти. Для этого реализован метод автоматического удаления — сборщик мусора. При традиционном удалении через null происходит лишь устранение ссылки на объект, а сам объект удаляется. Существуют методы принудительной сборки мусора, хотя они не рекомендованы для использования в обычной работе.

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

Модификаторы

Различают разные типы модификаторов. Кроме тех, которые определяют способ доступа, существует модификаторы методов, переменных, класса. Методы, объявленные как private, доступны только в объявленном классе. Такие переменные не могут использоваться в других классах и функциях. Public открывает доступ для любого класса. Если нужно получить Public class из другого пакета, то его следует предварительно импортировать.

Модификатор protected похож по действую на public — открывает доступ к полям класса. В обоих случаях переменные могут использоваться в других классах. Но паблик-модификатор доступен абсолютно для всех, а протектед-модификатор — только для наследуемых классов.

Модификатор, который применяется при создании методов, — static. Это означает, что созданный метод существует независимо от экземпляров класса. Модификатор Final не управляет доступом, а указывает на невозможность дальнейшего манипулирования значениями объекта. Он запрещает изменение того элемента, для которого указан.

Final для полей делает невозможным изменение первого значения переменной:

public static void mthod(String[] args) <

final int Name = 1;

int Name = 2;// выдаст ошибку

Переменные с модификатором final являются константами. Их принято писать только заглавными буквами. CamelStyle и другие способы не работают.

Final для методов указывает запрет на изменение метода в наследуемом классе:

final void myMethod() <

Final для классов означает, что нельзя создать наследников класса:

final public class Class <

Abstract — модификатор создания абстрактных классов. Любой абстрактный класс и абстрактные методы предназначены для дальнейшего расширения в других классах и блоках. Модификатор transient указывает виртуальной машине не обрабатывать заданную переменную. В этом случае та просто не сохранится. Например, transient int Name = 100 не сохранится, а int b сохранится.

Платформы и версии

Существующие семейства языка программирования Java:

  • Standard Edition.
  • Enterprise Edition.
  • Micro Edition.
  • Card.
  1. SE — является основным, широко используется для создания пользовательских приложений для индивидуального использования.
  2. EE — набор спецификаций для разработки ПО уровня предприятия. Содержит больше возможностей, чем SE, поэтому используется в коммерческих масштабах на крупных и средних предприятиях.
  3. ME — предназначены для устройств с ограниченной мощностью и памятью, у них, как правило, малый размер дисплея. Такими устройствами являются смартфоны и КПК, ресиверы цифрового телевидения.
  4. Card — предназначена для устройств с крайне ограниченными вычислительными ресурсами, например таких, как смарт-карты, sim-карты, банкоматы. Для этих целей был изменен байт-код, требования к платформе, составляющее библиотек.

Применение

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

Активно используется для создания мобильных приложений под Android. Программа компилируется в нестандартный байт-код, выполняется на виртуальной машине ART. Для компиляции применяется Android Studio. Это IDE от компании «Гугл» является официальной для разработки под ОС Android.

Microsoft разработала собственную реализацию виртуальной машины Java MSJVM. Она имела такие отличия, которые ломали основополагающую концепцию кроссплатформенности — отсутствовала поддержка некоторых технологий и методов, имелись нестандартные расширения, работающие только на платформе Windows. Microsoft выпустил язык J#, синтаксис и работа в целом которого очень напоминает Java. Он не соответствовал официальной спецификации и в итоге был исключен из стандартного инструментария разработчика Microsoft Visual Studio.

Язык программирования Java и среда

Разработка программного обеспечения осуществляется в таких IDE:

  1. JDK.
  2. NetBeans IDE.
  3. Eclipse IDE.
  4. IntelliJ IDEA.
  5. JDeveloper.
  6. Java для iOS.
  7. Geany.

JDK распространяется компанией Oracle как комплект разработчика на языке Java. Включает компилятор, стандартные библиотеки, утилиты, исполнительную систему. Современные интегрированные среды разработки опираются именно JDK.

Удобно писать код на языке программирования Java в среде Netbeans и Eclipse IDE. Это свободные интегрированные среды для разработки, они подходят под все платформы «Джава». Также используются для программирования на Python, PHP, JavaScript, C++.

IntelliJ IDE от компании Jetbrains распространяется в двух вариантах: бесплатном и коммерческом. Поддерживает написание кода на многих языках программирования, существуют сторонние плагины от разработчиков, в которых реализовано еще большее количество ЯП.

JDeveloper — еще одна разработка от компании Oracle. Полностью написана на Java, поэтому работает на всех операционных системах.

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих