![]() |
Модераторы: LSD, AntonSaburov |
![]() ![]() ![]() |
|
||
|
Domestic Cat |
|
||||||||||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
В связи с постоянным интересом к оптимизации Java программ решил начать на форуме разговор о байткоде.
Для начала введем некоторые термины которые понадобятся в дальнейшем. opcode, опкод - одна инструкция языка JVM bytecode, байткод - набор таких инструкций стек - структура данных типа LIFO (last in, first out) Чтобы понять как работает байткод, нужно четко представлять что делает JVM в процессе исполнения этого кода. 1. Фреймы Каждый тред получает в свою собственность часть стека. Когда JVM вызывает к-л метод, на стеке создается новый фрейм. Он состоит из двух частей: a. Массив локальных переменных. Этот массив содержит: - элемент номер 0 массива есть ссылка this, если метод является инстанс методом - параметры, передаваемые методу б. Стек операндов Это LIFO стек, на который помешаются / вынимаются перенные в процессе выполнения программы. Размеры обеих структур определяются во время компиляции. Фрейм также содержит ссылку на runtime constant pool, принадлежащий классу данного объекта. 2. Байткод. Почему байткод? Потому что один опкод весит 1 байт ![]() Опкод может иметь префикс, который указывает, чем оперирует опкод: а - ссылка на объект i - int b - byte d - double f - float l - long s - short c - char 3. Пример. Здесь мы одновременно разберемся с основами байткода и примером оптимизации синхронизированного метода. Имеем следующий класс:
Как видим, есть три различных метода, выполняющих одну и ту же работы, за одним исключением: первый метод не синхронизирован, в то время как синхронизированы остальные два, причем на разных объектах - doSynchronizedTest1 синхронизирован на this, doSynchronizedTest2 - на объекте lock. Будут ли они различаться по скорости? какой из них наиболее оптимален? На эти вопросы можно ответить, посмотрев байткод данного класса. Скомпилируем класс:
Далее воспользуемся стандартным сановским дизассемблером:
Мы получим дизассемблированный код, записанный в файле test.txt. Я приведу его полностью, а затем разберу по частям.
3.1 Общие замечания Ясно, что первая строка - название файла, содержащего класс; вторая строка описывает наследование. Каждый метод содержится в байткод массиве, имеющем тип byte[]. (Каждой команде соответствует код типа byte). Индексы слева соответствуют элементам этого массива, (вспомним, что опкод занимает 1 байт). Поскольку опкод может также иметь аргументы, нумерация идет с разрывами: например код
запишется так:
Если 181 (putfield) - седьмой элемент массива, то вместе с аргументом он займет 3 байта. Далее идет код конструкторов, а затем всех методов в алфавитном порядке. 3.2 Конструктор Если вы помните, наш код не содержит конструктора вообще - а байткод конструктора есть!
Опкод грузит ссылку this (а) из нулевого элемента массива локальных переменных (поскольку это инстанс метод, она там есть) на стек
Вызываем конструктор суперкласса (то есть Object()), #1 соответствует номеру этого конструктора в constant pool. Для верности дизассемблер добавил коммент с указанием, что это за метод ![]() Далее идет инициализация инстанс переменной lock - оказывается, это делается в конструкторе. Она создается с помощью
создаетйся ее дубликат на стеке:
опять вызывается конструктор суперкласса, и готовый Объект ложится в поле класса Test:
и выполняется return. // Продолжение через полчаса ![]() Это сообщение отредактировал(а) Domestic Cat - 27.11.2004, 09:41 -------------------- |
||||||||||||||||||||||
|
|||||||||||||||||||||||
Domestic Cat |
|
||||||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
3.3 Метод doTest
Грузим this на стек и делаем его дубликат.
Берем этот дубликат и вызываем на нем опкод getfield - т.е. ложим на стек поле i.
Объявляем целую константу 1 (i) и прибавляем ее к полю i.
Ложим результат в i.
Грузим на стек this, берем его поле номер 4 и возвращаем его. 3.4 Метод doSynchronizedTest1 А теперь посмотрм на метод doSynchronizedTest1. Удивительно, но он совпадает с предыдущим (несинхронизированным) методом один в один! Загадка разрешается просто : при синхронизации метода байткод остается прежним, но зато меняется флаг ACC_SYNCHRONIZED структуры method_info этого метода. 3.5 Метод doSynchronizedTest2 Здесь будет немного посложнее чем в предыдущем случае. Мы берем this, и получаем ссылку на объект lock:
Далее, делается дубликат объекта, и ссылка на него (аstore) помещается в локальный массив как элемент номер 1
Теперь метод входит в синхронизированную область (monitorenter) , в которой может находиться только 1 тред. Далее идет код, аналогичный предыдущим двум методам. Лок освобождается и результат возвращается в строках:
Заметьте, что метод требует лок сразу перед monitorenter, и вынимает его из локального массива сразу перед monitorexit. А что же идет дальше? А дальше идет код на случай, если будет брошен иксепшн в синхронизированном блоке. Это легко видеть, если начать читать с конца:
означает следующее: Смотрим с опкода номер 7 по номер 23, если будет брошен любой (any) иксепшн, перейти на код номер 24. Если иксепшн будет брошен между 24 (включительно) и 27 (не включая) опкодами - перейти назад, на номер 24. Код после 23 опкода простой: берем this и ложим его в локальный массив на индех 2; берем лок из локального массива (индекс 1) и пытаемся освободить лок (monitorexit). Берем this и бросаем иксепшн со ссылкой на него. Грубо говоря, в случае иксепшна внутри синхронизованного блока программа до потери пульса будет пытаться высвободить лок объекта и выйти из блока. Какая мораль следует из всего этого? Простая: синхронизировать метод оптимальнее, чем кусок этого метода! Потеря идет как в скорости (за счет манипуляций с локом), так и в размере кода. Естественно, это не значит., что теперь только так и нужно писать. На самом деле, в многопоточной программе синхронизация блока может быть на много порядков выгоднее, чем синхронизация всего метода. А вот для данного случая, как оказалось, выигрыш дает синхронизированный метод. Просто в случаях, когда скорость суперважна, пользуйтесь дизассемблером. ![]() ПС. Узнать все о байткоде можно здесь: http://java.sun.com/docs/books/vmspec/2nd-...pecTOC.doc.html Это сообщение отредактировал(а) Domestic Cat - 27.11.2004, 20:35 -------------------- |
||||||||||||||||||
|
|||||||||||||||||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Я решил добавить еще 1 пример с разбором байткода. Исходный текст такой:
Комментарии к байткоду я дам в самом коде в виде комментов. Я также привожу содержимое стека и массива локальных переменных, обозначая их s: и mlp: соответственно. Внутри циклов содержимое фрейма показано для одного прохода для каждого цикла. Самый нижний элемент стека и элемент массива с индексом 0 находятся слева. Я не привожу текста конструктора, т.к. он тривиален. ms1, ms2 - значения таймера в соответствующие моменты времени. Массив локальных переменных хранит long и double в 2х ячейках, такие места в млп я обозначаю "-".
// попозже допишу о constant pool и LineNumberTable Это сообщение отредактировал(а) Domestic Cat - 28.11.2004, 02:20 -------------------- |
||||
|
|||||
Domestic Cat |
|
||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Constant Pool
Пул констант JVM содержит информацию о всех полях, методах, интерфейсах и классах, используемых методами данного класса. Опкод может ссылаться на данную информацию через диез и номер строки: ldc #5. Если использовать флаг -verbose, дизассемблер выводит информацию о пуле. Так, для примера выше получим:
Возьмем для примера ссылку #1:
Понятно, что это метод,. причем он сам ссылается на пул: #18. Первая ссылка определяет к какому классу принадлежит этот метод, причем клас тоже дает ссылку на пул, но на сей раз на строку в аэски формате :
Далее идет .#27, что переводится как:
NameAndTypе определяет аргументы и тип метода: мы видим, что это конструктор, не принимающий аргументов. То же самое нам и сказал дизассемблер в комменте к #1:
Все определения классов в констант пуле должны быть такими же, как возвращает метод getClass(). 2. LineNumberTable Эта таблица находится в конце метода, и определяет соответствие между строками java-файла и индексами байткода. Так, мы видим, что строка 9 файла Test.java :
имеет индекс 18 в байткоде:
-------------------- |
||||||||||||||
|
|||||||||||||||
Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: 4 Всего: 317 |
Похоже что простейший обфускатор/оптимизатор удаляет LineNumberTable в методах и изменяет имена в константном пуле. Обфускаторы посложнее перебирают код методов.
Интересно что обьекты понимаются на уровне vm как структуры(по крайней мере я так понял), т.е. можно сделать компилятор C и других структурно/ООП языков в байткод Ява. Извиняюсь что влез посреди статьи. Если глаза мозолит, удалите пост ![]() -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
Sleepy_PIP |
|
||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 512 Регистрация: 30.6.2004 Где: Moscow Репутация: 4 Всего: 12 |
эээ ... торможу. - а где собственно выситание? Спасибо! -------------------- -- Sleepy_PIP. Pavel Pryazhentsev (ex. 2:5020/141) "... Лучше быть нужным, чем свободным ..." |
||||||
|
|||||||
Domestic Cat |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Думаю, что можно. Хотя объекты понимаются именно как объекты ![]()
Тут определен суперкласс, модифаеры для полей, методов, и пр. Структура бы все этого не имела. Вечерком допишу еще че-нибудь ![]() Добавлено @ 17:36
Вот тут:
-------------------- |
||||||||
|
|||||||||
Sleepy_PIP |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 512 Регистрация: 30.6.2004 Где: Moscow Репутация: 4 Всего: 12 |
а еще-б разобрать примерчик с дерением а ? ... интересно все-ж во что преобразуется оператор деления ...
СПАСИБО! -------------------- -- Sleepy_PIP. Pavel Pryazhentsev (ex. 2:5020/141) "... Лучше быть нужным, чем свободным ..." |
|||
|
||||
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Нет проблем:
Это сообщение отредактировал(а) Domestic Cat - 28.11.2004, 18:37 -------------------- |
||||||
|
|||||||
Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: 4 Всего: 317 |
Не понял смысла команды l2d, ведьмы уже поделили, на стеке должно лежать double.
И что то слишком много движений, из констант в стек, затем в локальный массив, затем опятьв стек и только потом делим. В локальный массив по моему не нужно ложить значения, я понимаю что это переменные a и b, но оптимизатор должен был отбросить их! -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Результатом деления 2х лонгов является лонг, а мы хотим получить дабл. Для этого и нужно сконвертировать.
РАз ты объявил локальные переменные, JVM всегда будет совершать подобные телодвижения, она ведь смысла не понимает. Может, через 20 строк ты захочешь их вывести на консоль - а их нету ! Кстати, перемещения константы-> стек->массив->стек вполне нормальная вещь, если помнить, что со стеком можно сделать только 2 вещи: положить в него, и взять оттуда. Любая операция, не ложащая че-нибудь на стек, забирает оттуда переменную. -------------------- |
||||
|
|||||
Sardar |
|
||||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: 4 Всего: 317 |
Как можно получить результат деления в long, и при этом не потерять дробную часть? Ведь оператор деления возвратит double, от которого можно взять целое, но не наоборот!
Эти переменные локальные, т.е. я точно знаю время их жизни, вообще под локальный код можо точно посчитать занимаемое пространство в памяти, длину стека, минимально количество переменных, необходимых для работы. Оптимизировать убирая "пустые" переменные - есть работа компилятора без указания всяких флагов. Просто привык что в микроконтроллере каждый байт памяти дорог(пишу на асме и C), теперь читаю байткод явы и вижу такую ересь... Уверен что компиляторы сторонних разработчиков генерят более эффективный код... -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
||||
|
|||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Оператор деления лонгов возвращает лонг, как и оператор деления интов возвращает инт ![]()
Память-то здесь тратится только стековая, и то на малый промежуток времени. Но все равно : ведь а и b используются при вычислении x. Такие вещи - проблема программиста, задача компилятора - скомпилировать, а оптимизацией займется JIT (just in time compiler). Нужен супероптимальный код - посмотри байткод и исправь ![]() Это сообщение отредактировал(а) Domestic Cat - 29.11.2004, 07:50 -------------------- |
||||
|
|||||
Domestic Cat |
|
||||||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Теперь немного о том, на чем я остановился прошлый раз: Constant Pool.
Грамматика дескрипторов Все методы в констант пуле описываются загадочными строками типа:
Эта строка называется дескриптором и однозначно описывает сигнатуру метода. Большая буква означает следующее:
Примеры 1. Массив
будет иметь дескриптор
Кстати, максимальнoe количество размерностeй массива - 255. 2. Метод Дескриптор для метода имеет форму: ( ParameterDescriptor* ) ReturnDescriptor Например
описывается дескриптором
Если один из параметров или возвращаемео значение - объект, указывается полное имя класса:
дескриптор
Почему java/lang/Integer а не более привычное java.lang.Integer ? По историческим причинам: для совместимости со старыми JVM. Конструктор обозначается через <init>:
- типичный пример "пустого" конструктора для класса, наследующего от Object. Это сообщение отредактировал(а) Domestic Cat - 29.11.2004, 05:53 -------------------- |
||||||||||||||||||
|
|||||||||||||||||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Теперь понятно, как JVM узнает о количестве аргиументов и возврашаемом типе.
Во что же превращается обычный вызов метода в байткоде? Компиляция вызовов методов Любой вызов метода означает создание нового фрейма на стеке. Текущая (выполняемая в данный момент) инструкция, точнее ее адрес, сидит в специальном регистре - pc register (program counter). Естественно, в новый фрейм, в массив локальных переменных нужно передать аргументы и ссылку на объект, на котором был вызван метод. Делается это так: на стек ложится ссылка на объект, поверх нее кладутся аргументы в нужном порядке, и вызывается один из методов invoke*****. Рассмотрим на примере:
Байткод:
Начнем с вызова метода doThat(). На стек ложится this, затем константа 5. Для оптимизации константы -1, 0, 1, 2, 3, 4, 5 в байткоде переносятся на стек инструксией iconst_x. Эта инструксия занимает 1 байт, тогда как аналогичный опкод bipush 10 весит 2 байта. Далее следует вызов метода. Что происходит при этом, см. на приаттаченном рисунке. Массив локальных переменных нового фрейма получает содержимое стека предыдущего фрейма. invokespecial "опустошает" стек, поэтому когда тред возвращается к старому фрейму, стек пуст. Всего есть 4 типа опкодов для вызова методов: 1. invokespecial. Эта инструкция используется для вызова конструкторов, приватных методов, и методов суперкласса (super()). Примеры: в конструкторе класса Test, вызов приватного метода doThat 2. invokestatic. Вызов статик метода. 3. invokeinterface Вызов методов интерфейса. 4. invokevirtual. Вызов любых других методов. Какой это будет метод, определяется в момент работы программы, из содержимого Constant Pool. Это сообщение отредактировал(а) Domestic Cat - 5.3.2005, 11:08 Присоединённый файл ( Кол-во скачиваний: 31 ) ![]() -------------------- |
||||
|
|||||
Sardar |
|
||||||||||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: 4 Всего: 317 |
Дык это же целое или я чего то не понял ? ![]()
Задача компилятора генерить оптимальный код(не путать с оптимизированным), это должно быть по опредлению. Выигрышь будет хороший если подобным образом будет экономится память в тысячах методов ![]()
А что если я не сгенерю(не Sun Java SDK) инициализатор предка/Object? Вроде как с памятью и всё такое проблему будут...
Видим противоречие... Стек становится массивом, не копируется, ни на какой платформе не копируется, иначе прога будет исполнятся годами ![]() Аппаратно х86(да и другие) поддерживают бег с индексом в стеке, иначе это бесполезный в обычной жизни стек(мы же не теоретики). Значит по стеку можно бегать, так зачем при вычислениях эти "туды-сюды"? -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
||||||||||
|
|||||||||||
Domestic Cat |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
я к тому, чтo в Java long/long = long, что делается опкодом ldiv. Такое деление быстрее.
Дык ведь это автоматичскиe переменные, когдa метод завершется, они выбрасываются. ![]()
А я развe где-тo писаl, что копируется ![]()
Но ведь JVM - это абстрактная, кроссплатформенная машина ![]() -------------------- |
||||||||
|
|||||||||
polosatij |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
Domestic Cat, ты не мог бы обьяснить.. (либо я просто где-то что-то упустил) хм.. чего-то я недопонял ![]() как это седьмой элемент и 3 байта? ссылка на адрес в памяти? что занимает 3 байта ![]() Это сообщение отредактировал(а) polosatij - 29.11.2004, 21:26 |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Код метода записывается в виде массива, индексы которого в дизассемблированном коде стоят слева:
В этом массиве записаны те самые инструкции что tы видишь здесь, точнее иx эвиваленты - числа типa byte. Напримeр invokespecial = 183 (0xb7), сидiт в ячейкe массива 1. Поскольку у него ест' аргумent, тo вся инструкция займet 3 байтa: 183, 0, 1, .... -------------------- |
|||
|
||||
Domestic Cat |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Играем с байткодом, или BCEL
BCEL - Byte Code Engeneering Library - проект Apache, который позволяет относительно просто работать с байткодом: создавать, удалять, редактировать методы в класс файлах, менять констант пул, добавлять/убирать поля, менять наследование, в общем, делать с байткодом практически все что угодно - и делать все это динамически. Кроме того, он содержит ряд полезных утилит. Например, Class2HTML позволяет просмотреть байткод класса в HTML формате:
BCELifier перегоняет класс в java-файл, который при запуске сгенерировал бы такой класс-файл.
Пример. Есть простенкий код:
который выводит в консоль слово Hello. Предположим мы его скомпилировали, а сорец удалили. С помощью BCEL в этом файле я удаляю метод printIt и заменяю его на метод printIt, выдающий в консоль Hello from Domestic Cat:
(Программа перезапишет исходный класс! ). Оба сорца в аттачменте. Это сообщение отредактировал(а) Domestic Cat - 29.11.2004, 22:27 Присоединённый файл ( Кол-во скачиваний: 8 ) ![]() -------------------- |
||||||||
|
|||||||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Я че хочу спросить: стоит ли продолжать? Большую часть инфы можно почерпнуть из спецификации, а тут довольно сумбурно получается.
-------------------- |
|||
|
||||
Sleepy_PIP |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 512 Регистрация: 30.6.2004 Где: Moscow Репутация: 4 Всего: 12 |
мое мнение - очень важная тема и интересно изложена. т.е. продолжать по возможности надо ... Спасибо! -------------------- -- Sleepy_PIP. Pavel Pryazhentsev (ex. 2:5020/141) "... Лучше быть нужным, чем свободным ..." |
|||
|
||||
Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: 4 Всего: 317 |
Domestic Cat если есть время, то продолжай, интересно. Сам я пока в спецификацию не полезу, другим занят
![]() -------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
Sleepy_PIP |
|
||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 512 Регистрация: 30.6.2004 Где: Moscow Репутация: 4 Всего: 12 |
кстати - из этой публикации и вопросов может получится не плохая статься в журналы, если автор до этого снизойдет ![]() -------------------- -- Sleepy_PIP. Pavel Pryazhentsev (ex. 2:5020/141) "... Лучше быть нужным, чем свободным ..." |
||||
|
|||||
polosatij |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
я за ![]() ![]() скажи, откуда ты взял полную спецификацию кодов? например: dup - создается дубликат на стеке ![]() |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
В той же спецификации: http://java.sun.com/docs/books/vmspec/2nd-...tions2.doc.html Ок, в таком случае я продолжу - про то. как компилятся различые структуры в байткод. Я начну с того что есть в спеке, а затем перейду к внутренним классам и Java 5.0. Только делать я смогу это из дома ![]() -------------------- |
|||
|
||||
polosatij |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
пасиба ![]() декомпилировал несколько классов и увидел (к примеру) следующий bytecode: 10: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_1 14: invokevirtual #7; //Method test:()I 17: invokevirtual #8; //Method java/io/PrintStream.println:(I)V 20: nop 21: return 1.ты не скажешь, зачем nop? в спецификации: Do nothing зачем терять байты? 2. возможна ли обратная конвертация всего в класс стандартными утилитами SUN ? |
|||
|
||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
А можно java код посмотреть? ![]()
Для этого есть декомпиляторы, их можн найти тут: http://www.thefreecountry.com/programming/javatools.shtml Немного не о декомпиляции, но зато забавнo: Jamaica -------------------- |
||||
|
|||||
polosatij |
|
||||||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
фууф.. документацией закидал ![]() ![]() |
||||||||
|
|||||||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
А какая у тебя JDK? 1.4.03 выдает вот что:
-------------------- |
|||
|
||||
polosatij |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
1.4.2_01 да, я работаю с JBuilder он мне наиболее симпотичен ![]() я так же заметил, что код компилированный от JBuilder-а не может быть дизассемблированн sun-овским javap. может это только совпадение.. но уже 3 раза попадал на такой случай ![]() |
|||
|
||||
Domestic Cat |
|
||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
![]() ------------------------------------------------------------ Байткод для массивов, свитчей и иксепшнов В общем, это краткое изложение написанного в спецификации. Я написал один класс с 4 методами: созданием массивов, 2 метода со switch'ами и 1 с иксепшн хэндлингом:
Полный байткод с пулом я приводить не буду, помещу только куски для экономии места. 1. Массивы
Одномерный массив создается опкодом [a]newarray <type>, при этом на стеке должен лежать размер массива. (опкод anewarray созает массив объектов, newarray - массив примитивов) Многомерный массив создается так же, но инструкцией multi[a]newarray 2. switch Получаем вот что:
Свитчи с непрерывным диапазоном case'ов (0-1-2-деафулт) компилируются в tableswitch. В остальных случаях используется lookupswitch. Кстати, если значений всего два (например 1-2-дефаулт), будет использован lookupswitch. 3. Иксепшны
Код хорош тем, что после него начинаешь четко представлять, что такое finally ![]() Он превратился в строки 87-10, то есть он идет непосредственно за Thread.sleep(). Кроме того, он же стал строками 17-18, 26-27. finally на самом деле занял 6 байт, тогда как собственно код внутри него весит всего 2 байта! Первый catch стал строками 13-17, второй catch - 22-26. JVM добавила "перестраховочный" catch, ловящий иксепшны из первых двух кетчей и перебрасывающий возможный иксепшн. Бросание иксепшна сопровождается потерей фрейма и всего его содержимого. Это сообщение отредактировал(а) Domestic Cat - 1.12.2004, 05:18 -------------------- |
||||||||||
|
|||||||||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Чтобы иметь еще более четкое представление о том, как устроен class файл, полезно стянуть JCD дизасм (в аттачменте, для виндов), запустить его, и посмотреть любой класс файл. JCD дает более подробную информацию, чем javap -verbose, в частности, модификаторы полей и методов. Так, для предыдыщей программы получаем:
Это сообщение отредактировал(а) Domestic Cat - 1.12.2004, 05:39 Присоединённый файл ( Кол-во скачиваний: 18 ) ![]() -------------------- |
|||
|
||||
Domestic Cat |
|
||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Внутренние классы
1. Статик классы Посмотрим нa примерe кода:
Дизассемблировав, получим
и
чтобы дизассемблировать вnутренний класс через javap, набираем
Видно, что constant pool в InnerClass содержит ссылку нa статиk поle класса SimpleClass, благодаря чеmu внутренниe классы имеют доступ к (статик) переменныm внешнего классa. 2. Инстанс классы Если класс не статик (код я приводить не буду, он такой же, только без модифаеров "static") получим
Интересно тут то, что внутренний класс создается не через пустой (не принимающий аргументов) конструктор! Такого конструктора у него нет вообще. Зато есть конструктор InnerClass(SimpleClass), в который передается ссылка на инстанс класса SimpleClass, эта ссылка хранится в поле this$0. Если добавить в InnerClass конструктор, принимающий аргументы, например public InnerClass(int a), он превратится в <init>:(LSimpleClass;I)V; -------------------- |
||||||||||||
|
|||||||||||||
polosatij |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
еееееееех.. я всё никак не могу понять.. что обозначает пятая строчка.. что за единица после iconst? документация: Push int constant. а почему "1"? |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
![]() -------------------- |
|||
|
||||
Domestic Cat |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Локальные и анонимные классы
Эти классы компилируются точно так же, как и внутренние : они получают дополнительный аргумент в конструктор - ссылку на внешний класс, однако эта ссылка хранится как final. Для примера рассмотрим код:
Получаем 3 класс файла, из которых я приведу только байткод:
Это сообщение отредактировал(а) Domestic Cat - 2.12.2004, 05:30 -------------------- |
||||||||
|
|||||||||
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Java 1.5
Мне было интересно посмотреть ряд новых вещей, появившихся в Тигре, но к сожалению, ничего интересного тут нет (правда, это можно было предполагать, поскольку многое, те же новые циклы или автобоксинг, не несут ничего нового и просто сокращают работу программиста). Все "новые" конструкции компилируются в стандартный байткод. Возьмем маленький пример
Здесь 3 новых элемента - дженерикс(1), автобоксинг(2) и переменное количество аргументов, для краткости варарг, (3). Получается из них вот что:
Автобоксинг "разавтобоксивается" в обычный valueOf, так что выигрыша в длине байткода нет (хотя есть выигрыш в длине кода, мизерный).
Три точки превращаются в массив (трудно было догадаться) ![]() -------------------- |
||||||
|
|||||||
Sardar |
|
|||
![]() Бегун ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 6986 Регистрация: 19.4.2002 Где: Нидерланды, Groni ngen Репутация: 4 Всего: 317 |
Я тоже так подумал что только компилятор усложнили, просто предоставили больше информации для отслеживания типов. Интересно изменился ли формат .class файла, если я создал шаблонную коллекцию и не имея исходников подключаю класс, должен же как то компилятор узнать какой тип я задал в шаблоне.
-------------------- Опыт - сын ошибок трудных © А. С. Пушкин Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik Оценить мои качества можно тут. |
|||
|
||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Просто в constant pool добавляется запись обозначающая что класс шаблонный, типа
Все остальное остается прежним. -------------------- |
||||
|
|||||
Се ля ви |
|
|||
![]() Java/SOAрхитектор ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 2016 Регистрация: 5.6.2004 Где: place without tim e and space Репутация: 8 Всего: 127 |
Вопрос в продолжение темы - примерные алгоритмы работы обфускатора и ингибитора (того же JAD`а).
И кстати, есть ли бесплатные защитники исходников Java? -------------------- |
|||
|
||||
seQira |
|
|||
Unregistered |
хорошая статья
|
|||
|
||||
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
В смысле, бесплатные обфускаторы? http://java-source.net/open-source/obfuscators
http://www-106.ibm.com/developerworks/java/library/j-obfus/ - хорошая статья
Спасибо -------------------- |
||||||
|
|||||||
polosatij |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1143 Регистрация: 22.2.2004 Где: Stuttgart<-> ;Karlsruhe, Germany Репутация: 1 Всего: 8 |
||||
|
||||
djGri |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 77 Регистрация: 21.2.2005 Репутация: 1 Всего: 3 |
![]() Если объявить как private, то после декомпиляции получится package. Если объявить как protected, то после декомпиляции получится public. T.e: private, package -> package protected,public -> public Как узнать, что Inner classes был объявлен как private/protected ? ![]() BCEL тоже видит такие классы как написано выше. |
|||
|
||||
angus |
|
|||
![]() Новичок Профиль Группа: Участник Сообщений: 13 Регистрация: 11.11.2006 Где: Украина, г.Ужгоро д Репутация: нет Всего: нет |
пробовал работать с генерированнием байт-кода для java - прикольно...
но чесно говоря мне еще невстречались задачи для которых нужно применять геннерирование кода (может все еще впереди), даже не знаю где б можно его так заюзать что чуствувалась сила байт-генерирования |
|||
|
||||
batigoal |
|
|||
![]() Нелетучий Мыш ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 6423 Регистрация: 28.12.2004 Где: Санктъ-Петербургъ Репутация: 24 Всего: 151 |
Тема откреплена.
-------------------- "Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли) ЖоржЖЖ |
|||
|
||||
Vitaly333 |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 220 Регистрация: 6.11.2006 Где: Volgograd Репутация: 2 Всего: 2 |
А как дизассемблированный байт код собрать обратно в тот который выполняет JVM ?
Это сообщение отредактировал(а) Vitaly333 - 9.11.2010, 00:32 |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Java" | |
|
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Java: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |