Верификация Java байткода:
как, когда, а может
отключить?
Никита Липский
Excelsior LLC
2
OS+CPU
Monitoring
AOT
Bytecode
Classloading
engine
Bytecode Verification
Execution engine:
interpreter, JIT
Threading
Synchronization
Meta
information
Memory management,
Garbage Collection
Native methods
Анатомия JVM
3
Кто знает про себя ?
• Более 20 лет профессиональной карьеры
• Инициатор проекта Excelsior JET
– работал над проектом более 17 лет
– как идейный вдохновитель
– компиляторный инженер
– руководитель
– и много в каких еще ролях
• twitter: @pjBooms
4
Из доклада вы узнаете
1. Как и когда работает верификатор
2. Может ли верификатор байткода замедлить
исполнение программы и ее старт
3. Что бы было, если бы не было верификатора
4. Почему опасно его отключать
5. Как избежать VerifyError при порождении
байткода
5
Java class file & bytecode
6
Java class file
Класс файл Пример: Constant Pool
7
Версия
Constant Pool
Имя класса, модификаторы
Суперкласс, суперинтрфейсы
Поля
Методы
Атрибуты
CONSTANT_Integer: 100500
CONSTANT_Float: 3.1415
CONSTANT_String : Hello World!
CONSTANT_Class: java.lang.String
CONSTANT_FieldRef: A.field@int
CONSTANT_MethodRef: B.method(IJ)F
CONSTANT_InterfaceMethodRef: I.foo()V
Java class file
• Поля, методы тоже имеют атрибуты
(например, значения константных полей)
• Главный атрибут метода – это его код: Java
байт-код
8
Java bytecode
• Массив инструкций
• Стэк операндов инструкций метода
• Массив локальных переменных (аргументы
метода, локальные переменные)
9
Java bytecode
Инструкция берет свои операнды со стэка и кладет
результат на стэк. Пример:
10
0: iload 3
2: bipush 5
4: iadd
5: istore 4
7: …
#3
2:I _:?
#4
Java bytecode
Инструкция берет свои операнды со стэка и кладет
результат на стэк. Пример:
11
0: iload 3
2: bipush 5
4: iadd
5: istore 4
7: …
#3
2:I _:?
#4
2:I
Java bytecode
Инструкция берет свои операнды со стэка и кладет
результат на стэк. Пример:
12
0: iload 3
2: bipush 5
4: iadd
5: istore 4
7: …
#3
2:I _:?
#4
2:I
5:I
Java bytecode
Инструкция берет свои операнды со стэка и кладет
результат на стэк. Пример:
13
0: iload 3
2: bipush 5
4: iadd
5: istore 4
7: …
#3
2:I _:?
#4
7:I
Java bytecode
Инструкция берет свои операнды со стэка и кладет
результат на стэк. Пример:
14
0: iload 3
2: bipush 5
4: iadd
5: istore 4
7: …
#3
2:I 7:I
#4
C: StackOverflowError
A: Программа зациклится B: VerifyError
D: Не будет исполняться
Что произойдет при исполнении этого байт-кода?
Загрузка класс-файла.
Когда работает верификатор?
16
Стадии загрузки класса в JVM
• Загрузка (loading)
• Линковка (linking)
• Инициализация (initializing)
Глава “5. Loading, Linking, and Initializing”
спецификации JVM
Процесс загрузки класса
(создание класса)
• Читается class file
– Проверяется корректность формата (может
выбросить ClassFormatError)
• Создается ран-тайм представление класса в
выделенной области памяти
– runtime constant pool in Method Area aka Meta
Space aka Permanent Generation
• Грузятся суперкласс, суперинтерфейсы
18
Линковка
• Верификация байт-кода
• Подготовка
• Разрешение символьных ссылок
19
Разрешение символьных ссылок
A.java:
class A {
B useB;
int f1 = B.f;
int f2 = B.foo();
}
B.java:
class B {
static int f;
static int foo(){};
}
Разрешение символьных ссылок
21
A.class
…
CONSTANT_Class: B
CONSTANT_FieldRef: B.f@int
CONSTANT_MethodRef: B.foo()I
…
B.class
…
Field: f@int
Method: foo()I
…
Разрешение символьных ссылок
Класс может иметь ссылки на другие классы и поля,
методы других классов
• Ленивое разрешение
– ссылки разрешаются при первом доступе
• Энергичное разрешение
– разрешаются все ссылки какие возможно
22
Инициализация класса
23
class A {
static int i = 10;
static String s = "Hello";
static {
System.out.println(s);
}
}
A.class
…
<clinit>:
i = 10;
s = "Hello";
System.out.println(s);
…
Вызов статического инициализатора класса
Порядок стадий загрузки класса*
• Класс должен быть полностью загружен прежде,
чем слинкован
• Класс должен быть полностью проверифицирован
и подготовлен, прежде чем проинициализирован
• Ошибки разрешения ссылок, должны
происходить, когда ссылка реально требуется
* Глава 5.4 Спецификации JVM
Порядок стадий загрузки класса*
• Класс должен быть полностью загружен прежде,
чем слинкован
• Класс должен быть полностью проверифицирован
и подготовлен, прежде чем проинициализирован
• Ошибки разрешения ссылок, должны
происходить, когда ссылка реально требуется
* Глава 5.4 Спецификации JVM
Порядок стадий загрузки класса*
• Класс должен быть полностью загружен прежде,
чем слинкован
• Класс должен быть полностью проверифицирован
и подготовлен, прежде чем проинициализирован
• Ошибки разрешения ссылок, должны
происходить, когда ссылка реально требуется
* Глава 5.4 Спецификации JVM
Порядок стадий загрузки класса*
Вывод: верификация класса происходит до того как какой-либо
байткод какого-либо метода этого класса исполнится
Loading Verification Preparation Initializing
t
Resolution
Execution
Порядок стадий загрузки класса*
Вывод: верификация класса происходит до того как какой-либо
байткод какого-либо метода этого класса исполнится
Loading Verification Preparation Initializing
t
Resolution
Execution
C: StackOverflowError
A: Программа зациклится B: VerifyError
D: Не будет исполняться
Что произойдет при исполнении этого байт-кода?
Как?
• Картинка
30
Верификация Java байткода
Верификация Java байткода
Верификация Java байткода:
• проверка на статические ограничения (static constraints)
• проверка на структурные ограничения (structural
constraints)
32
Статические ограничения
• байткод не пуст и короче 65536 байт
• все инструкции идут одна за другой (нет “дыр”)
• все инструкции имеют известную JVM мнемонику (opcode)
• переходы внутри байткода не выходят за его пределы и
указывают на начало инструкции (не в середину)
• локальные переменные загружаются из корректных слотов
регистра локальных переменных (не выходят за его пределы)
• И т.д.
33
Статические ограничения
Проверяются за два прохода:
• Строится массив инструкций, каждая инструкция имеет
адрес (смещение внутри байткода)
• Проверяется, что все переходы корректны
На выходе получается управляющий граф (CFG) байткода
метода
34
Структурные ограничения
• Глубина стека в каждой инструкции должна быть
определенной величины для любого пути исполнения
35
Структурные ограничения
• Глубина стека в каждой инструкции должна быть
определенной величины для любого пути исполнения
• При исполнении любой инструкции не должна нарушится
типовая совместимость по присваиванию (assign
compatibility).
36
Структурные ограничения
• Глубина стека в каждой инструкции должна быть
определенной величины для любого пути исполнения
• При исполнении любой инструкции не должна нарушится
типовая совместимость по присваиванию (assign
compatibility).
– Пример: для инструкции putstatic C.f на вершине стэка должно
лежать значение, тип которого совместим по присваиванию с
типом поля C.f (для любого пути исполнения данной инструкции)
37
Структурные ограничения
Структурные ограничения оперируют термином “для любого
пути исполнения”. Однако верификация байткода должна
происходить до исполнения байткода.
Вопрос: Как это возможно?
Ответ: С помощью статического потокового анализа
38
Структурные ограничения
Структурные ограничения оперируют термином “для любого
пути исполнения”. Однако верификация байткода должна
происходить до исполнения байткода.
Вопрос: Как это возможно?
Ответ: С помощью статического потокового анализа
39
Статический потоковый анализ
40
Пример: вывод типов
val a = new T()
val b = a
41
new T()
a:?
b:?
Пример: вывод типов
val a = new T()
val b = a
42
new T()
a:?
b:?
Пример: вывод типов
val a = new T()
val b = a
43
new T()
a:T
b:?
Пример: вывод типов
val a = new T()
val b = a
44
new T()
a:T
b:T
Пример: проверка типов
val a = new T()
T2 b = a
45
new T()
a:?
b:T2
Пример: проверка типов
val a = new T()
T2 b = a
46
new T()
a:T
b:T2
Пример: проверка типов
val a = new T()
T2 b = a
47
new T()
a:T
b:T2
CHECK: T is assignable to T2
Пример: преобразование типов
val a = new T()
val b = (T2)a
48
new T()
(T2)
a
b:?
Пример: преобразование типов
val a = new T()
val b = (T2)a
49
new T()
(T2)
a
b:?
Пример: преобразование типов
val a = new T()
val b = (T2)a
50
new T()
(T2)
a
b:Т2
Пример: слияние типов
val a = cond?
new T1() :
new T2()
51
cond?
new
T1()
a:?
new T2()
Пример: слияние типов
val a = cond?
new T1() :
new T2()
52
cond?
new
T1()
a:?
new T2()
Пример: слияние типов
val a = cond?
new T1() :
new T2()
53
cond?
new
T1()
a:T1
new T2()
Пример: слияние типов
val a = cond?
new T1() :
new T2()
54
cond?
new
T1()
a:T1
new T2()
Пример: слияние типов
val a = cond?
new T1() :
new T2()
55
cond?
new
T1()
a:T1⋀T2
new T2()
Пример: условия, циклы
val a = null
val b = null
while (cond1) {
a = (cond2) ?
b :
new T1();
b = (cond3) ?
a :
new T2();
}
56
Статический потоковый анализ
Статический потоковый анализ имеет дело с:
57
Статический потоковый анализ
Статический потоковый анализ имеет дело с:
• Потоковым графом (управляющий граф программы)
58
Статический потоковый анализ
Статический потоковый анализ имеет дело с:
• Потоковым графом (управляющий граф программы)
• Пометками на вершинах (свойства, вычисляемые факты)
– Начальная разметка – известные факты в начале
59
Статический потоковый анализ
Статический потоковый анализ имеет дело с:
• Потоковым графом (управляющий граф программы)
• Пометками на вершинах (свойства, вычисляемые факты)
– Начальная разметка – известные факты в начале
• Свойства текут от вершины к вершине
60
Статический потоковый анализ
Статический потоковый анализ имеет дело с:
• Потоковым графом (управляющий граф программы)
• Пометками на вершинах (свойства, вычисляемые факты)
– Начальная разметка – известные факты в начале
• Свойства текут от вершины к вершине
• Свойства преобразуются на входе в вершину и сливаются
с уже имеющимися у вершины
61
62
Полурешетка свойств
Полурешетка – <L,⋀>:
x, y, z ∈ L
1. x ⋀ x = x (самоприменимость)
2. x ⋀ y = y ⋀ x (коммутативность)
3. x ⋀ (y ⋀ z) = (x ⋀ y) ⋀ z (ассоциативность)
Частичный порядок – ≤:
x ≤ y ⇔def x ⋀ y = x
x < y ⇔def x ⋀ y = x & x ≠ y
Пример: множества
A B
B < A
A BA⋀B
C:=A⋀B, C<A, C<B
Полурешетка свойств
Полурешетка свойств – это ограниченная решетка с
обрывающимися строго убывающими цепями:
1.  x1> x2 >… , k: ∄y ∈ L, xk > y
2. x, bx :  chain ∈ {x> x1 > …> xk}, |chain|< bx (ограниченность),
BL := max {bx} – высота полурешетки
3. ∈ L:  ⋀ x = , x∈ L (нуль, “дно”)
4. ⊤∈ L: ⊤ ⋀ x =x, x∈ L (единица, "штопор")
Примеры полурешеток
• Множества
• Полурешетка (!) иерархии классов
⋀ := LCA (lowest common ancestor)
Полурешетка иерархии классов
67

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
68

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
69

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
70

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
71

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
72

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
73

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Полурешетка иерархии классов
74

A F
B C G
D E
⊤
B extends AA B :=
 := j.l.Object
⊤ := null
B C⋀ = A
D E⋀ = C
B E⋀ = A
Потоковые функции
Пусть <L,⋀> - полурешетка свойств
f: L → L – монотонная функция:
x, y ∈ L, x ≤ y ⇒ f(x) ≤ f(y)
Свойство GKUW (Graham, Kam, Ullman, Wegman):
f – монотонная функция ⇔
x, y ∈ L: f(x⋀y) ≤ f(x)⋀f(y)
f – дистрибутивная функция ⇔def
x, y ∈ L: f(x⋀y) = f(x)⋀f(y)
Потоковые функции
Пусть <L,⋀> - полурешетка свойств
f: L → L – монотонная функция:
x, y ∈ L, x ≤ y ⇒ f(x) ≤ f(y)
Свойство GKUW (Graham, Kam, Ullman, Wegman):
f – монотонная функция ⇔
x, y ∈ L: f(x⋀y) ≤ f(x)⋀f(y)
f – дистрибутивная функция ⇔def
x, y ∈ L: f(x⋀y) = f(x)⋀f(y)
Потоковые функции
Пусть <L,⋀> - полурешетка свойств
f: L → L – монотонная функция:
x, y ∈ L, x ≤ y ⇒ f(x) ≤ f(y)
Свойство GKUW (Graham, Kam, Ullman, Wegman):
f – монотонная функция ⇔
x, y ∈ L: f(x⋀y) ≤ f(x)⋀f(y)
f – дистрибутивная функция ⇔def
x, y ∈ L: f(x⋀y) = f(x)⋀f(y)
Пример дистрибутивной функции
Операция преобразования типа (cast):
(Т)v
Дистрибутивная функция:
Cast(T, Tv) := T ⋀ Tv, где Tv := Type(v)
Cast(T, Tv1 ⋀ Tv2) == Cast(T, Tv1) ⋀ Cast(T, Tv2)
78
Пример дистрибутивной функции
Операция преобразования типа (cast):
(Т)v
Дистрибутивная функция:
Cast(T, Tv) := T < Tv ? Tv : T, где Tv := Type(v)
Cast(T, Tv1 ⋀ Tv2) == Cast(T, Tv1) ⋀ Cast(T, Tv2)
79
Пример дистрибутивной функции
Операция преобразования типа (cast):
(Т)v
Дистрибутивная функция:
Cast(T, Tv) := T < Tv ? Tv : T, где Tv := Type(v)
Cast(T, Tv1 ⋀ Tv2) == Cast(T, Tv1) ⋀ Cast(T, Tv2)
80
Постановка задачи потокового анализа
DF=<G, Env, Init, PT> - задача потокового анализа:
G := <V, E, entry ∈V > - потоковый граф
Env := <L,⋀,F> - окружение потокового анализа
Init: V → L – начальная разметка
PT: V → F - преобразователи свойств
(для вершины v ∈ V будем записывать PTv)
Постановка задачи потокового анализа
( MOP решение)
DF = <G = <V, E>, Env = <L,⋀,F>, Init, PT> – задача
потокового анализа
Meet-over-all-paths (пересечение по всем путям) – (желаемое)
решение задачи потокового анализа
MOP(v) = ⋀p ∈Path(v) fp (⊤),
Path(v) = {p ∈ Paths(entry, v)| v∈V}
fp ⇔def fvn
∘… ∘ fv1
, p =(v1 .. vn) ∈ Paths(v1,vn)
fv ∈F ⇔def fv(l) = PTv(l) ⋀ Init(v), l ∈L
83
lv1
lv
PTv
PTv1
84
lv1
lv
PTv
PTv1
lv1
85
lv1
lv
PTv
PTv1
lv1
86
lv1
PTv(lv1
)⋀ lv
PTv
PTv1
87
lv1
l'v
PTv
PTv1
Приближенное решение
(стационарная разметка – MFP)
Правило разметки (ПР):
Пусть lv ∈ L – текущая пометка вершины v,
(v1 , v) ∈ E
l’v =def PTv(lv1
)⋀ lv – следующая пометка вершины v
Достижимая разметка :
M: V → L: получается из Init применением *(ПР)
Стационарная разметка (MFP):
Достижимая разметка не меняющаяся после любого применения ПР
lv1
PTv(lv1
)⋀ lv
PTv
PTv1
Свойства MFP
1. MFP DF = <G, Env, Init, PT> (сходимость)
2. !MFP DF = <G, Env, Init, PT> (единственность)
3. MFP(v) ≤ MOP(v), v∈V
(безопасность/корректность)
Свойства MFP
Теорема Килдалла
Let DF =<G, Env = <L,⋀,F> , Init, PT>:
f ∈ F ⇒ f – дистрибутивная(!), тогда:
MFP(v) = MOP(v), v∈V
(оптимальность)
Теорема о неразрешимости
Не существует алгоритма, который бы строил точное
решение (MOP) для любой задачи потокового анализа:
∄A . A вычисляет MOP
DF = <G, Env, Init, PT>
(неразрешимость)
Верификация байткода методом вывода
типов (type inference verifier)
Верификация байткода методом вывода типов
Type inference verifier – потоковый анализатор:
• Потоковый граф – управляющий граф (CFG)
• Решетка свойств – множество StackMapFrame
• Преобразователи свойств определены для каждой
инструкции байткода, согласно их семантики
93
Stack Map Frame
Stack Map Frame – свойство, вычисляемое для каждой
инструкции данного байткода метода:
• Stack: глубина и типы значений на стеке
94
Stack Map Frame
Stack Map Frame – свойство, вычисляемое для каждой
инструкции данного байткода метода:
• Stack: глубина и типы значений на стеке
• Locals: типы локальных переменных в регистре локальных
переменных
95
Пример
public static int add(int a, int b) { return a + b; }
96
0: iload 0
2: iload 1
4: iadd
5: ireturn
#0
int
#1
int
Пример
public static int add(int a, int b) { return a + b; }
97
0: iload 0
2: iload 1
4: iadd
5: ireturn
#0
int
#1
int
int
Пример
public static int add(int a, int b) { return a + b; }
98
0: iload 0
2: iload 1
4: iadd
5: ireturn
#0
int
#1
int
int
int
Пример
public static int add(int a, int b) { return a + b; }
99
0: iload 0
2: iload 1
4: iadd
5: ireturn
#0
int
#1
int
int
Пример
public static int add(int a, int b) { return a + b; }
100
0: iload 0
2: iload 1
4: iadd
5: ireturn
#0
int
#1
int
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
101
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
102
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn int
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
103
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
104
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn A
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
105
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn
#0
A
#1
int
#2
B
A
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
106
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
107
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn B
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
108
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn B A⋀
#0
A
#1
int
#2
B
0
4
9
8
Пример 2
class A extends C{}; class B extends C{};
public C ifcond(boolean cond, A a, B b) {return cond?a:b;}
109
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: areturn C
#0
A
#1
int
#2
B
0
4
9
8
Пример 3
110
SD: 0
SD: ?
SD: ?
SD: ?
SD: Stack Depth
Пример 3
111
SD: 0
SD: 1
SD: ?
SD: ?
SD: Stack Depth
Пример 3
112
SD: 0
SD: 1
SD: 2
SD: ?
SD: Stack Depth
Пример 3
113
SD: 0
SD: 1
SD: 2
SD: 1
SD: Stack Depth
Пример 3
114
SD: 0
SD: 1
SD: 2
SD: 1
SD: Stack Depth
Can’t merge
SD=1 with SD=0
Not verified!
Верификация байткода методом вывода типов
Каждая инструкция байткода определяет преобразователь
свойств:
F(opcode): StackMapFrame -> StackMapFrame
115
Верификация байткода методом вывода типов
Эти преобразователи дистрибутивны!
116
Верификация байткода методом вывода типов
Эти преобразователи дистрибутивны!
Значит верификатор строит точное решение задачи
верификации за конечное время без исполнения байткода!
117
Верификация байткода методом вывода типов
Эти преобразователи дистрибутивны!
Значит верификатор строит точное решение задачи
верификации за конечное время без исполнения байткода!
Но за какое время?
118
Оценка сложности алгоритма MFP
Утверждение:
Если обходить потоковый граф в глубину, то временная
сложность алгоритма MFP (AMFP):
T(AMFP) = O(BL*A*|V|),
Где A – количество обратных дуг в потоковом графе
A =O(|V|), на практике A ≤ 3
Верификация байткода методом вывода типов
Время работы верификатора зависит от:
• Количества инструкций
• Количества циклов
• Количества локальных переменных
• Максимальной глубины стека операндов
• Высоты иерархии классов
Дорого?
120
Верификация байткода методом вывода типов
Время работы верификатора зависит от:
• Количества инструкций
• Количества циклов
• Количества локальных переменных
• Максимальной глубины стека операндов
• Высоты иерархии классов
Дорого?
121
Верификация байткода методом
проверки типов (type checking verifier)
Верификация байткода методом
проверки типов
Вопрос: Как ускорить верификатор?
Идея: А что если мы знаем MFP решение
верификатора методом вывода типов?
Bingo! Тогда нам достаточно за один проход по байткоду
проверить, что неподвижная точка (MFP)
действительно неподвижная!
(не меняется при применении преобразователей свойств инструкций)
123
Верификация байткода методом
проверки типов
Вопрос: Как ускорить верификатор?
Идея: А что если мы знаем MFP решение
верификатора методом вывода типов?
Bingo! Тогда нам достаточно за один проход по байткоду
проверить, что неподвижная точка (MFP)
действительно неподвижная!
(не меняется при применении преобразователей свойств инструкций)
124
Верификация байткода методом
проверки типов
Вопрос: Как ускорить верификатор?
Идея: А что если мы знаем MFP решение
верификатора методом вывода типов?
Bingo! Тогда нам достаточно за один проход по байткоду
проверить, что неподвижная точка (MFP)
действительно неподвижная!
(не меняется при применении преобразователей свойств инструкций)
125
Верификация байткода методом
проверки типов
Split Verifier aka Type checking verifier:
• У байткода версии Java 5 может быть атрибут
StackMapTable (с Java 7 обязан быть)
126
Верификация байткода методом
проверки типов
Split Verifier aka Type checking verifier:
• У байткода версии Java 5 может быть атрибут
StackMapTable (с Java 7 обязан быть)
• StackMapTable – это список StackMapFrame
127
Верификация байткода методом
проверки типов
Split Verifier aka Type checking verifier:
• У байткода версии Java 5 может быть атрибут
StackMapTable (с Java 7 обязан быть)
• StackMapTable – это список StackMapFrame
• StackMapFrame есть для каждой начальной инструкции
линейных участков байткода, кроме первого
128
Верификация байткода методом
проверки типов
Split aka Type checking verifier за один проход по байткоду:
• вычисляет недостающие StackMapFrame: для каждой
инструкции проверяет входящие типы, вычисляет
исходящие (переносит ниже)
• проверяет что StackMapFrame из StackMapTable атрибута
не меняются при переносе StackMapFrame из инструкций
выше (проверка неподвижности точки)
129
Верификация байткода методом
проверки типов
Split aka Type checking verifier за один проход по байткоду:
• вычисляет недостающие StackMapFrame: для каждой
инструкции проверяет входящие типы, вычисляет
исходящие (переносит ниже)
• проверяет что StackMapFrame из StackMapTable атрибута
не меняются при переносе StackMapFrame из инструкций
выше (проверка неподвижности точки)
130
Роль верификатора байткода в JVM
Роль верификатора байткода в JVM
Вопросы:
1. Может ли верификатор байткода замедлить исполнение ?
2. Может ли верификатор байткода замедлить старт?
3. Что было бы, если бы не было верификатора байткода?
4. А что будет, если его отключить?
132
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение?
А исполнение чего?
• Исполнение байткода метода
• Исполнение горячего кода программы
• Исполнение программы
133
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение?
А исполнение чего?
• Исполнение байткода метода
• Исполнение горячего кода программы
• Исполнение программы
134
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение?
А исполнение чего?
• Исполнение байткода метода
• Исполнение горячего кода программы
• Исполнение программы
135
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение байткода метода?
Вспомним: Верификатор работает до исполнения!
Ответ: Нет!
136
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение байткода метода?
Вспомним: Верификатор работает до исполнения байткода!
Ответ: Нет!
137
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение байткода метода?
Вспомним: Верификатор работает до исполнения байткода!
Ответ: Нет!
138
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение программы?
139
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение программы?
Верификатор работает во время загрузки класса, загрузка
классов работает во время исполнения программы
140
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение программы?
Верификатор работает во время загрузки класса, загрузка
классов работает во время исполнения программы
Ответ: вообще говоря, да
141
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение горячего кода?
142
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение горячего кода?
Во время исполнения горячего кода, все классы, с которым
он работает, были загружены пока код был холодным.
143
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить
исполнение горячего кода?
Во время исполнения горячего кода, все классы, с которым
он работает, были загружены пока код был холодным.
Ответ: нет
144
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить старт
программы?
145
Верификатор и время старта
0
2
4
6
8
10
12
14
16
18
20
Eclipse 4.6 Cold Start Eclipse 4.6 Warm Start
verify on
verify:none
146
Время старта Eclipse 4.6 в секундах
Верификатор и время старта
0
10
20
30
40
50
60
70
80
90
IntellIj IDEA 2016.3 Cold Start IntellIj IDEA 2016.3 Warm Start
verify on
verify:none
147
Время старта Intellij IDEA 2016.3 в секундах
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить старт
программы?
148
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить старт
программы?
Ответ: незначительно
149
Роль верификатора байткода в JVM
Вопрос: Может ли верификатор байткода замедлить старт
программы?
Ответ: незначительно
У JVM c AOT компилятором верификация на старте может не
происходить
150
Роль верификатора байткода в JVM
Вопрос: Что было бы, если бы не было верификатора?
151
Роль верификатора байткода в JVM
Вопрос: Что было бы, если бы не было верификатора?
Ответ: пришлось бы вставлять проверки во время исполнения:
152
Роль верификатора байткода в JVM
Вопрос: Что было бы, если бы не было верификатора?
Ответ: пришлось бы вставлять проверки во время исполнения:
• для инструкций работающих со стеком, проверять что стек
не пуст и не переполнится
153
Роль верификатора байткода в JVM
Вопрос: Что было бы, если бы не было верификатора?
Ответ: пришлось бы вставлять проверки во время исполнения:
• для инструкций работающих со стеком, проверять что стек
не пуст и не переполнится
• для того, чтобы сложить два числа проверять, что на стеке
лежат два числа
154
Роль верификатора байткода в JVM
Вопрос: Что было бы, если бы не было верификатора?
Ответ: пришлось бы вставлять проверки во время исполнения:
• для инструкций работающих со стеком, проверять что стек
не пуст и не переполнится
• для того, чтобы сложить два числа проверять, что на стеке
лежат два числа
Как бы быстро тогда работали Java программы?
155
Роль верификатора байткода в JVM
Факт: Интерпретатор не проверяет выходы за пределы стека
операндов и типовую совместимость: если надо сложить два
числа, он просто берет их со стека и складывает.
156
Роль верификатора байткода в JVM
Факт: Интерпретатор не проверяет выходы за пределы стека
операндов и типовую совместимость: если надо сложить два
числа, он просто берет их со стека и складывает.
Факт: JIT компилятор при генерации машинного кода тоже не
вставляет такие проверки в машинный код.
157
Роль верификатора байткода в JVM
Факт: Интерпретатор не проверяет выходы за пределы стека
операндов и типовую совместимость: если надо сложить два
числа, он просто берет их со стека и складывает.
Факт: JIT компилятор при генерации машинного кода тоже не
вставляет такие проверки в машинный код.
Вывод: Наличие верификатора – первопричина почему JVM
исполняет Java программы быстро!
158
Роль верификатора байткода в JVM
Вопрос: А что будет, если верификатор отключить?
# A fatal error has been detected by the Java Runtime Environment:
Exception in thread "main" java.lang.StackOverflowError
at test.foo(test.j)
at test.main(test.j:3)
#
# Internal Error (javaCalls.cpp:53), pid=8012, tid=16396
# guarantee(!thread->is_Compiler_thread()) failed: cannot make java calls
from the compiler
#
159
Роль верификатора байткода в JVM
Вопрос: А что будет, если верификатор отключить?
# A fatal error has been detected by the Java Runtime Environment:
Exception in thread "main" java.lang.StackOverflowError
at test.foo(test.j)
at test.main(test.j:3)
#
# Internal Error (javaCalls.cpp:53), pid=8012, tid=16396
# guarantee(!thread->is_Compiler_thread()) failed: cannot make java calls
from the compiler
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
160
Роль верификатора байткода в JVM
Вопрос: А что будет, если верификатор отключить?
Ответ: Отключение верификатора может приводить к
развалам JVM, что в свою очередь открывает
потенциальные дыры в безопасности.
161
Роль верификатора байткода в JVM
Вопрос: А что будет, если верификатор отключить?
Ответ: Отключение верификатора может приводить к
развалам JVM, что в свою очередь открывает
потенциальные дыры в безопасности.
Вывод: Наличие верификатора делает исполнение байткода
не только эффективным, но и безопасным!
162
Верификатор и библиотеки генерации
байткода
Библиотеки генерации байткода
• Низкоуровневые
– ASM
– BCEL
• Высокоуровневые
– Javassist
– cglib
– ByteBuddy
164
ASM библиотека
ASM библиотека
• Позволяет полностью контролировать содержимое
выходных классов, байткода
165
ASM библиотека
ASM библиотека
• Позволяет полностью контролировать содержимое
выходных классов, байткода
• Как следствие низкоуровневая: вы работаете
непосредственно с инструкциями байткода
166
ASM библиотека
ASM: пример кода
MethodVisitor methodVisitor = … ;
methodVisitor.visitVarInsn(Opcodes.ILOAD, 3);
methodVisitor.visitIntInsn (Opcodes.BIPUSH, 5);
methodVisitor.visitInsn (Opcodes.IADD);
methodVisitor.visitVarInsn(Opcodes.ISTORE, 4);
167
ASM библиотека
• Нужно знать семантику байткодных инструкций
• Легко породить неверифицируемый байткод
• ASM умеет вычислять StackMapTable атрибут
– с помощью того же потокового анализа
• Можно задавать StackMapTable атрибут руками
– для скорости; автоматическое вычисление замедляет ASM в 2 р.
168
ASM библиотека
• Нужно знать семантику байткодных инструкций
• Легко породить неверифицируемый байткод
• ASM умеет вычислять StackMapTable атрибут
– с помощью того же потокового анализа
• Можно задавать StackMapTable атрибут руками
– для скорости; автоматическое вычисление замедляет ASM в 2 р.
169
ASM библиотека
• Нужно знать семантику байткодных инструкций
• Легко породить неверифицируемый байткод
• ASM умеет вычислять StackMapTable атрибут
– с помощью того же потокового анализа
• Можно задавать StackMapTable атрибут руками
– для скорости; автоматическое вычисление замедляет ASM в 2 р.
170
ASM библиотека
• Нужно знать семантику байткодных инструкций
• Легко породить неверифицируемый байткод
• ASM умеет вычислять StackMapTable атрибут
– с помощью того же потокового анализа
• Можно задавать StackMapTable атрибут руками
– для скорости; автоматическое вычисление замедляет ASM в 2
раза
171
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеют вычислять
StackMapFramе
172
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеют вычислять
StackMapFramе
В результате вы можете получить ошибку:
java.lang.VerifyError: Expecting a stackmap frame at branch
173
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеют вычислять
StackMapFramе
Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier
174
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеют вычислять
StackMapFramе
Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier
Тогда будет работать Type Inference Verifier
175
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеют вычислять
StackMapFramе
Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier
Тогда будет работать Type Inference Verifier
– Развал JVM не случится
176
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеет вычислять
StackMapFramе
Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier
Тогда будет работать Type Inference Verifier
– Но старый верификатор не знает про invokedynamic
– Случится VerifyError при использовании лямбд
177
Библиотеки генерации байткода
Проблема: некоторые (старые) библиотеки,
манипулирующие байткодом, не умеет вычислять
StackMapFramе
Решение: не использовать старые библиотеки.
Используя ASM, вычислять StackMapFramе самим или с
помощью ASM.
178
Пример
179
Заключение
Верификатор Java байткода:
• Незаслуженно обделенная вниманием часть JVM
• Первопричина почему JVM исполняет программы быстро
(практически на уровне низкоуровневых языков)
• Гарантирует безопасность исполнения
• Отключать верификатор не имеет никакого практического
cмысла
• Зная как работает верификатор, порождать байткод легко
180
Заключение
Верификатор Java байткода:
• Незаслуженно обделенная вниманием часть JVM
• Первопричина почему JVM исполняет программы быстро
(практически на уровне низкоуровневых языков)
• Гарантирует безопасность исполнения
• Отключать верификатор не имеет никакого практического
cмысла
• Зная как работает верификатор, порождать байткод легко
181
Заключение
Верификатор Java байткода:
• Незаслуженно обделенная вниманием часть JVM
• Первопричина почему JVM исполняет программы быстро
(практически на уровне низкоуровневых языков)
• Гарантирует безопасность исполнения
• Отключать верификатор не имеет никакого практического
cмысла
• Зная как работает верификатор, порождать байткод легко
182
Заключение
Верификатор Java байткода:
• Незаслуженно обделенная вниманием часть JVM
• Первопричина почему JVM исполняет программы быстро
(практически на уровне низкоуровневых языков)
• Гарантирует безопасность исполнения
• Отключать верификатор не имеет никакого практического
cмысла
• Зная как работает верификатор, порождать байткод легко
183
Заключение
Верификатор Java байткода:
• Незаслуженно обделенная вниманием часть JVM
• Первопричина почему JVM исполняет программы быстро
(практически на уровне низкоуровневых языков)
• Гарантирует безопасность исполнения
• Отключать верификатор не имеет никакого практического
cмысла
• Зная как работает верификатор, порождать байткод легко
184
Заключение
Верификатор Java байткода:
• Незаслуженно обделенная вниманием часть JVM
• Первопричина почему JVM исполняет программы быстро
(практически на уровне низкоуровневых языков)
• Гарантирует безопасность исполнения
• Отключать верификатор не имеет никакого практического
cмысла
• Зная как работает верификатор, порождать байткод легко
185
Вопросы и ответы
Никита Липский,
Excelsior
nlipsky@excelsior-usa.com
twitter: @pjBooms 186

Верификация Java байткода: когда, как, а может отключить?

  • 1.
    Верификация Java байткода: как,когда, а может отключить? Никита Липский Excelsior LLC
  • 2.
    2 OS+CPU Monitoring AOT Bytecode Classloading engine Bytecode Verification Execution engine: interpreter,JIT Threading Synchronization Meta information Memory management, Garbage Collection Native methods Анатомия JVM
  • 3.
  • 4.
    Кто знает просебя ? • Более 20 лет профессиональной карьеры • Инициатор проекта Excelsior JET – работал над проектом более 17 лет – как идейный вдохновитель – компиляторный инженер – руководитель – и много в каких еще ролях • twitter: @pjBooms 4
  • 5.
    Из доклада выузнаете 1. Как и когда работает верификатор 2. Может ли верификатор байткода замедлить исполнение программы и ее старт 3. Что бы было, если бы не было верификатора 4. Почему опасно его отключать 5. Как избежать VerifyError при порождении байткода 5
  • 6.
    Java class file& bytecode 6
  • 7.
    Java class file Классфайл Пример: Constant Pool 7 Версия Constant Pool Имя класса, модификаторы Суперкласс, суперинтрфейсы Поля Методы Атрибуты CONSTANT_Integer: 100500 CONSTANT_Float: 3.1415 CONSTANT_String : Hello World! CONSTANT_Class: java.lang.String CONSTANT_FieldRef: A.field@int CONSTANT_MethodRef: B.method(IJ)F CONSTANT_InterfaceMethodRef: I.foo()V
  • 8.
    Java class file •Поля, методы тоже имеют атрибуты (например, значения константных полей) • Главный атрибут метода – это его код: Java байт-код 8
  • 9.
    Java bytecode • Массивинструкций • Стэк операндов инструкций метода • Массив локальных переменных (аргументы метода, локальные переменные) 9
  • 10.
    Java bytecode Инструкция беретсвои операнды со стэка и кладет результат на стэк. Пример: 10 0: iload 3 2: bipush 5 4: iadd 5: istore 4 7: … #3 2:I _:? #4
  • 11.
    Java bytecode Инструкция беретсвои операнды со стэка и кладет результат на стэк. Пример: 11 0: iload 3 2: bipush 5 4: iadd 5: istore 4 7: … #3 2:I _:? #4 2:I
  • 12.
    Java bytecode Инструкция беретсвои операнды со стэка и кладет результат на стэк. Пример: 12 0: iload 3 2: bipush 5 4: iadd 5: istore 4 7: … #3 2:I _:? #4 2:I 5:I
  • 13.
    Java bytecode Инструкция беретсвои операнды со стэка и кладет результат на стэк. Пример: 13 0: iload 3 2: bipush 5 4: iadd 5: istore 4 7: … #3 2:I _:? #4 7:I
  • 14.
    Java bytecode Инструкция беретсвои операнды со стэка и кладет результат на стэк. Пример: 14 0: iload 3 2: bipush 5 4: iadd 5: istore 4 7: … #3 2:I 7:I #4
  • 15.
    C: StackOverflowError A: Программазациклится B: VerifyError D: Не будет исполняться Что произойдет при исполнении этого байт-кода?
  • 16.
  • 17.
    Стадии загрузки классав JVM • Загрузка (loading) • Линковка (linking) • Инициализация (initializing) Глава “5. Loading, Linking, and Initializing” спецификации JVM
  • 18.
    Процесс загрузки класса (созданиекласса) • Читается class file – Проверяется корректность формата (может выбросить ClassFormatError) • Создается ран-тайм представление класса в выделенной области памяти – runtime constant pool in Method Area aka Meta Space aka Permanent Generation • Грузятся суперкласс, суперинтерфейсы 18
  • 19.
    Линковка • Верификация байт-кода •Подготовка • Разрешение символьных ссылок 19
  • 20.
    Разрешение символьных ссылок A.java: classA { B useB; int f1 = B.f; int f2 = B.foo(); } B.java: class B { static int f; static int foo(){}; }
  • 21.
    Разрешение символьных ссылок 21 A.class … CONSTANT_Class:B CONSTANT_FieldRef: B.f@int CONSTANT_MethodRef: B.foo()I … B.class … Field: f@int Method: foo()I …
  • 22.
    Разрешение символьных ссылок Классможет иметь ссылки на другие классы и поля, методы других классов • Ленивое разрешение – ссылки разрешаются при первом доступе • Энергичное разрешение – разрешаются все ссылки какие возможно 22
  • 23.
    Инициализация класса 23 class A{ static int i = 10; static String s = "Hello"; static { System.out.println(s); } } A.class … <clinit>: i = 10; s = "Hello"; System.out.println(s); … Вызов статического инициализатора класса
  • 24.
    Порядок стадий загрузкикласса* • Класс должен быть полностью загружен прежде, чем слинкован • Класс должен быть полностью проверифицирован и подготовлен, прежде чем проинициализирован • Ошибки разрешения ссылок, должны происходить, когда ссылка реально требуется * Глава 5.4 Спецификации JVM
  • 25.
    Порядок стадий загрузкикласса* • Класс должен быть полностью загружен прежде, чем слинкован • Класс должен быть полностью проверифицирован и подготовлен, прежде чем проинициализирован • Ошибки разрешения ссылок, должны происходить, когда ссылка реально требуется * Глава 5.4 Спецификации JVM
  • 26.
    Порядок стадий загрузкикласса* • Класс должен быть полностью загружен прежде, чем слинкован • Класс должен быть полностью проверифицирован и подготовлен, прежде чем проинициализирован • Ошибки разрешения ссылок, должны происходить, когда ссылка реально требуется * Глава 5.4 Спецификации JVM
  • 27.
    Порядок стадий загрузкикласса* Вывод: верификация класса происходит до того как какой-либо байткод какого-либо метода этого класса исполнится Loading Verification Preparation Initializing t Resolution Execution
  • 28.
    Порядок стадий загрузкикласса* Вывод: верификация класса происходит до того как какой-либо байткод какого-либо метода этого класса исполнится Loading Verification Preparation Initializing t Resolution Execution
  • 29.
    C: StackOverflowError A: Программазациклится B: VerifyError D: Не будет исполняться Что произойдет при исполнении этого байт-кода?
  • 30.
  • 31.
  • 32.
    Верификация Java байткода ВерификацияJava байткода: • проверка на статические ограничения (static constraints) • проверка на структурные ограничения (structural constraints) 32
  • 33.
    Статические ограничения • байткодне пуст и короче 65536 байт • все инструкции идут одна за другой (нет “дыр”) • все инструкции имеют известную JVM мнемонику (opcode) • переходы внутри байткода не выходят за его пределы и указывают на начало инструкции (не в середину) • локальные переменные загружаются из корректных слотов регистра локальных переменных (не выходят за его пределы) • И т.д. 33
  • 34.
    Статические ограничения Проверяются задва прохода: • Строится массив инструкций, каждая инструкция имеет адрес (смещение внутри байткода) • Проверяется, что все переходы корректны На выходе получается управляющий граф (CFG) байткода метода 34
  • 35.
    Структурные ограничения • Глубинастека в каждой инструкции должна быть определенной величины для любого пути исполнения 35
  • 36.
    Структурные ограничения • Глубинастека в каждой инструкции должна быть определенной величины для любого пути исполнения • При исполнении любой инструкции не должна нарушится типовая совместимость по присваиванию (assign compatibility). 36
  • 37.
    Структурные ограничения • Глубинастека в каждой инструкции должна быть определенной величины для любого пути исполнения • При исполнении любой инструкции не должна нарушится типовая совместимость по присваиванию (assign compatibility). – Пример: для инструкции putstatic C.f на вершине стэка должно лежать значение, тип которого совместим по присваиванию с типом поля C.f (для любого пути исполнения данной инструкции) 37
  • 38.
    Структурные ограничения Структурные ограниченияоперируют термином “для любого пути исполнения”. Однако верификация байткода должна происходить до исполнения байткода. Вопрос: Как это возможно? Ответ: С помощью статического потокового анализа 38
  • 39.
    Структурные ограничения Структурные ограниченияоперируют термином “для любого пути исполнения”. Однако верификация байткода должна происходить до исполнения байткода. Вопрос: Как это возможно? Ответ: С помощью статического потокового анализа 39
  • 40.
  • 41.
    Пример: вывод типов vala = new T() val b = a 41 new T() a:? b:?
  • 42.
    Пример: вывод типов vala = new T() val b = a 42 new T() a:? b:?
  • 43.
    Пример: вывод типов vala = new T() val b = a 43 new T() a:T b:?
  • 44.
    Пример: вывод типов vala = new T() val b = a 44 new T() a:T b:T
  • 45.
    Пример: проверка типов vala = new T() T2 b = a 45 new T() a:? b:T2
  • 46.
    Пример: проверка типов vala = new T() T2 b = a 46 new T() a:T b:T2
  • 47.
    Пример: проверка типов vala = new T() T2 b = a 47 new T() a:T b:T2 CHECK: T is assignable to T2
  • 48.
    Пример: преобразование типов vala = new T() val b = (T2)a 48 new T() (T2) a b:?
  • 49.
    Пример: преобразование типов vala = new T() val b = (T2)a 49 new T() (T2) a b:?
  • 50.
    Пример: преобразование типов vala = new T() val b = (T2)a 50 new T() (T2) a b:Т2
  • 51.
    Пример: слияние типов vala = cond? new T1() : new T2() 51 cond? new T1() a:? new T2()
  • 52.
    Пример: слияние типов vala = cond? new T1() : new T2() 52 cond? new T1() a:? new T2()
  • 53.
    Пример: слияние типов vala = cond? new T1() : new T2() 53 cond? new T1() a:T1 new T2()
  • 54.
    Пример: слияние типов vala = cond? new T1() : new T2() 54 cond? new T1() a:T1 new T2()
  • 55.
    Пример: слияние типов vala = cond? new T1() : new T2() 55 cond? new T1() a:T1⋀T2 new T2()
  • 56.
    Пример: условия, циклы vala = null val b = null while (cond1) { a = (cond2) ? b : new T1(); b = (cond3) ? a : new T2(); } 56
  • 57.
    Статический потоковый анализ Статическийпотоковый анализ имеет дело с: 57
  • 58.
    Статический потоковый анализ Статическийпотоковый анализ имеет дело с: • Потоковым графом (управляющий граф программы) 58
  • 59.
    Статический потоковый анализ Статическийпотоковый анализ имеет дело с: • Потоковым графом (управляющий граф программы) • Пометками на вершинах (свойства, вычисляемые факты) – Начальная разметка – известные факты в начале 59
  • 60.
    Статический потоковый анализ Статическийпотоковый анализ имеет дело с: • Потоковым графом (управляющий граф программы) • Пометками на вершинах (свойства, вычисляемые факты) – Начальная разметка – известные факты в начале • Свойства текут от вершины к вершине 60
  • 61.
    Статический потоковый анализ Статическийпотоковый анализ имеет дело с: • Потоковым графом (управляющий граф программы) • Пометками на вершинах (свойства, вычисляемые факты) – Начальная разметка – известные факты в начале • Свойства текут от вершины к вершине • Свойства преобразуются на входе в вершину и сливаются с уже имеющимися у вершины 61
  • 62.
  • 63.
    Полурешетка свойств Полурешетка –<L,⋀>: x, y, z ∈ L 1. x ⋀ x = x (самоприменимость) 2. x ⋀ y = y ⋀ x (коммутативность) 3. x ⋀ (y ⋀ z) = (x ⋀ y) ⋀ z (ассоциативность) Частичный порядок – ≤: x ≤ y ⇔def x ⋀ y = x x < y ⇔def x ⋀ y = x & x ≠ y
  • 64.
    Пример: множества A B B< A A BA⋀B C:=A⋀B, C<A, C<B
  • 65.
    Полурешетка свойств Полурешетка свойств– это ограниченная решетка с обрывающимися строго убывающими цепями: 1.  x1> x2 >… , k: ∄y ∈ L, xk > y 2. x, bx :  chain ∈ {x> x1 > …> xk}, |chain|< bx (ограниченность), BL := max {bx} – высота полурешетки 3. ∈ L:  ⋀ x = , x∈ L (нуль, “дно”) 4. ⊤∈ L: ⊤ ⋀ x =x, x∈ L (единица, "штопор")
  • 66.
    Примеры полурешеток • Множества •Полурешетка (!) иерархии классов ⋀ := LCA (lowest common ancestor)
  • 67.
    Полурешетка иерархии классов 67  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 68.
    Полурешетка иерархии классов 68  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 69.
    Полурешетка иерархии классов 69  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 70.
    Полурешетка иерархии классов 70  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 71.
    Полурешетка иерархии классов 71  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 72.
    Полурешетка иерархии классов 72  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 73.
    Полурешетка иерархии классов 73  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 74.
    Полурешетка иерархии классов 74  AF B C G D E ⊤ B extends AA B :=  := j.l.Object ⊤ := null B C⋀ = A D E⋀ = C B E⋀ = A
  • 75.
    Потоковые функции Пусть <L,⋀>- полурешетка свойств f: L → L – монотонная функция: x, y ∈ L, x ≤ y ⇒ f(x) ≤ f(y) Свойство GKUW (Graham, Kam, Ullman, Wegman): f – монотонная функция ⇔ x, y ∈ L: f(x⋀y) ≤ f(x)⋀f(y) f – дистрибутивная функция ⇔def x, y ∈ L: f(x⋀y) = f(x)⋀f(y)
  • 76.
    Потоковые функции Пусть <L,⋀>- полурешетка свойств f: L → L – монотонная функция: x, y ∈ L, x ≤ y ⇒ f(x) ≤ f(y) Свойство GKUW (Graham, Kam, Ullman, Wegman): f – монотонная функция ⇔ x, y ∈ L: f(x⋀y) ≤ f(x)⋀f(y) f – дистрибутивная функция ⇔def x, y ∈ L: f(x⋀y) = f(x)⋀f(y)
  • 77.
    Потоковые функции Пусть <L,⋀>- полурешетка свойств f: L → L – монотонная функция: x, y ∈ L, x ≤ y ⇒ f(x) ≤ f(y) Свойство GKUW (Graham, Kam, Ullman, Wegman): f – монотонная функция ⇔ x, y ∈ L: f(x⋀y) ≤ f(x)⋀f(y) f – дистрибутивная функция ⇔def x, y ∈ L: f(x⋀y) = f(x)⋀f(y)
  • 78.
    Пример дистрибутивной функции Операцияпреобразования типа (cast): (Т)v Дистрибутивная функция: Cast(T, Tv) := T ⋀ Tv, где Tv := Type(v) Cast(T, Tv1 ⋀ Tv2) == Cast(T, Tv1) ⋀ Cast(T, Tv2) 78
  • 79.
    Пример дистрибутивной функции Операцияпреобразования типа (cast): (Т)v Дистрибутивная функция: Cast(T, Tv) := T < Tv ? Tv : T, где Tv := Type(v) Cast(T, Tv1 ⋀ Tv2) == Cast(T, Tv1) ⋀ Cast(T, Tv2) 79
  • 80.
    Пример дистрибутивной функции Операцияпреобразования типа (cast): (Т)v Дистрибутивная функция: Cast(T, Tv) := T < Tv ? Tv : T, где Tv := Type(v) Cast(T, Tv1 ⋀ Tv2) == Cast(T, Tv1) ⋀ Cast(T, Tv2) 80
  • 81.
    Постановка задачи потоковогоанализа DF=<G, Env, Init, PT> - задача потокового анализа: G := <V, E, entry ∈V > - потоковый граф Env := <L,⋀,F> - окружение потокового анализа Init: V → L – начальная разметка PT: V → F - преобразователи свойств (для вершины v ∈ V будем записывать PTv)
  • 82.
    Постановка задачи потоковогоанализа ( MOP решение) DF = <G = <V, E>, Env = <L,⋀,F>, Init, PT> – задача потокового анализа Meet-over-all-paths (пересечение по всем путям) – (желаемое) решение задачи потокового анализа MOP(v) = ⋀p ∈Path(v) fp (⊤), Path(v) = {p ∈ Paths(entry, v)| v∈V} fp ⇔def fvn ∘… ∘ fv1 , p =(v1 .. vn) ∈ Paths(v1,vn) fv ∈F ⇔def fv(l) = PTv(l) ⋀ Init(v), l ∈L
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
    Приближенное решение (стационарная разметка– MFP) Правило разметки (ПР): Пусть lv ∈ L – текущая пометка вершины v, (v1 , v) ∈ E l’v =def PTv(lv1 )⋀ lv – следующая пометка вершины v Достижимая разметка : M: V → L: получается из Init применением *(ПР) Стационарная разметка (MFP): Достижимая разметка не меняющаяся после любого применения ПР lv1 PTv(lv1 )⋀ lv PTv PTv1
  • 89.
    Свойства MFP 1. MFPDF = <G, Env, Init, PT> (сходимость) 2. !MFP DF = <G, Env, Init, PT> (единственность) 3. MFP(v) ≤ MOP(v), v∈V (безопасность/корректность)
  • 90.
    Свойства MFP Теорема Килдалла LetDF =<G, Env = <L,⋀,F> , Init, PT>: f ∈ F ⇒ f – дистрибутивная(!), тогда: MFP(v) = MOP(v), v∈V (оптимальность)
  • 91.
    Теорема о неразрешимости Несуществует алгоритма, который бы строил точное решение (MOP) для любой задачи потокового анализа: ∄A . A вычисляет MOP DF = <G, Env, Init, PT> (неразрешимость)
  • 92.
    Верификация байткода методомвывода типов (type inference verifier)
  • 93.
    Верификация байткода методомвывода типов Type inference verifier – потоковый анализатор: • Потоковый граф – управляющий граф (CFG) • Решетка свойств – множество StackMapFrame • Преобразователи свойств определены для каждой инструкции байткода, согласно их семантики 93
  • 94.
    Stack Map Frame StackMap Frame – свойство, вычисляемое для каждой инструкции данного байткода метода: • Stack: глубина и типы значений на стеке 94
  • 95.
    Stack Map Frame StackMap Frame – свойство, вычисляемое для каждой инструкции данного байткода метода: • Stack: глубина и типы значений на стеке • Locals: типы локальных переменных в регистре локальных переменных 95
  • 96.
    Пример public static intadd(int a, int b) { return a + b; } 96 0: iload 0 2: iload 1 4: iadd 5: ireturn #0 int #1 int
  • 97.
    Пример public static intadd(int a, int b) { return a + b; } 97 0: iload 0 2: iload 1 4: iadd 5: ireturn #0 int #1 int int
  • 98.
    Пример public static intadd(int a, int b) { return a + b; } 98 0: iload 0 2: iload 1 4: iadd 5: ireturn #0 int #1 int int int
  • 99.
    Пример public static intadd(int a, int b) { return a + b; } 99 0: iload 0 2: iload 1 4: iadd 5: ireturn #0 int #1 int int
  • 100.
    Пример public static intadd(int a, int b) { return a + b; } 100 0: iload 0 2: iload 1 4: iadd 5: ireturn #0 int #1 int
  • 101.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 101 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn #0 A #1 int #2 B 0 4 9 8
  • 102.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 102 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn int #0 A #1 int #2 B 0 4 9 8
  • 103.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 103 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn #0 A #1 int #2 B 0 4 9 8
  • 104.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 104 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn A #0 A #1 int #2 B 0 4 9 8
  • 105.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 105 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn #0 A #1 int #2 B A 0 4 9 8
  • 106.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 106 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn #0 A #1 int #2 B 0 4 9 8
  • 107.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 107 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn B #0 A #1 int #2 B 0 4 9 8
  • 108.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 108 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn B A⋀ #0 A #1 int #2 B 0 4 9 8
  • 109.
    Пример 2 class Aextends C{}; class B extends C{}; public C ifcond(boolean cond, A a, B b) {return cond?a:b;} 109 0: iload_0 1: ifeq 8 4: aload_1 5: goto 9 8: aload_2 9: areturn C #0 A #1 int #2 B 0 4 9 8
  • 110.
    Пример 3 110 SD: 0 SD:? SD: ? SD: ? SD: Stack Depth
  • 111.
    Пример 3 111 SD: 0 SD:1 SD: ? SD: ? SD: Stack Depth
  • 112.
    Пример 3 112 SD: 0 SD:1 SD: 2 SD: ? SD: Stack Depth
  • 113.
    Пример 3 113 SD: 0 SD:1 SD: 2 SD: 1 SD: Stack Depth
  • 114.
    Пример 3 114 SD: 0 SD:1 SD: 2 SD: 1 SD: Stack Depth Can’t merge SD=1 with SD=0 Not verified!
  • 115.
    Верификация байткода методомвывода типов Каждая инструкция байткода определяет преобразователь свойств: F(opcode): StackMapFrame -> StackMapFrame 115
  • 116.
    Верификация байткода методомвывода типов Эти преобразователи дистрибутивны! 116
  • 117.
    Верификация байткода методомвывода типов Эти преобразователи дистрибутивны! Значит верификатор строит точное решение задачи верификации за конечное время без исполнения байткода! 117
  • 118.
    Верификация байткода методомвывода типов Эти преобразователи дистрибутивны! Значит верификатор строит точное решение задачи верификации за конечное время без исполнения байткода! Но за какое время? 118
  • 119.
    Оценка сложности алгоритмаMFP Утверждение: Если обходить потоковый граф в глубину, то временная сложность алгоритма MFP (AMFP): T(AMFP) = O(BL*A*|V|), Где A – количество обратных дуг в потоковом графе A =O(|V|), на практике A ≤ 3
  • 120.
    Верификация байткода методомвывода типов Время работы верификатора зависит от: • Количества инструкций • Количества циклов • Количества локальных переменных • Максимальной глубины стека операндов • Высоты иерархии классов Дорого? 120
  • 121.
    Верификация байткода методомвывода типов Время работы верификатора зависит от: • Количества инструкций • Количества циклов • Количества локальных переменных • Максимальной глубины стека операндов • Высоты иерархии классов Дорого? 121
  • 122.
  • 123.
    Верификация байткода методом проверкитипов Вопрос: Как ускорить верификатор? Идея: А что если мы знаем MFP решение верификатора методом вывода типов? Bingo! Тогда нам достаточно за один проход по байткоду проверить, что неподвижная точка (MFP) действительно неподвижная! (не меняется при применении преобразователей свойств инструкций) 123
  • 124.
    Верификация байткода методом проверкитипов Вопрос: Как ускорить верификатор? Идея: А что если мы знаем MFP решение верификатора методом вывода типов? Bingo! Тогда нам достаточно за один проход по байткоду проверить, что неподвижная точка (MFP) действительно неподвижная! (не меняется при применении преобразователей свойств инструкций) 124
  • 125.
    Верификация байткода методом проверкитипов Вопрос: Как ускорить верификатор? Идея: А что если мы знаем MFP решение верификатора методом вывода типов? Bingo! Тогда нам достаточно за один проход по байткоду проверить, что неподвижная точка (MFP) действительно неподвижная! (не меняется при применении преобразователей свойств инструкций) 125
  • 126.
    Верификация байткода методом проверкитипов Split Verifier aka Type checking verifier: • У байткода версии Java 5 может быть атрибут StackMapTable (с Java 7 обязан быть) 126
  • 127.
    Верификация байткода методом проверкитипов Split Verifier aka Type checking verifier: • У байткода версии Java 5 может быть атрибут StackMapTable (с Java 7 обязан быть) • StackMapTable – это список StackMapFrame 127
  • 128.
    Верификация байткода методом проверкитипов Split Verifier aka Type checking verifier: • У байткода версии Java 5 может быть атрибут StackMapTable (с Java 7 обязан быть) • StackMapTable – это список StackMapFrame • StackMapFrame есть для каждой начальной инструкции линейных участков байткода, кроме первого 128
  • 129.
    Верификация байткода методом проверкитипов Split aka Type checking verifier за один проход по байткоду: • вычисляет недостающие StackMapFrame: для каждой инструкции проверяет входящие типы, вычисляет исходящие (переносит ниже) • проверяет что StackMapFrame из StackMapTable атрибута не меняются при переносе StackMapFrame из инструкций выше (проверка неподвижности точки) 129
  • 130.
    Верификация байткода методом проверкитипов Split aka Type checking verifier за один проход по байткоду: • вычисляет недостающие StackMapFrame: для каждой инструкции проверяет входящие типы, вычисляет исходящие (переносит ниже) • проверяет что StackMapFrame из StackMapTable атрибута не меняются при переносе StackMapFrame из инструкций выше (проверка неподвижности точки) 130
  • 131.
  • 132.
    Роль верификатора байткодав JVM Вопросы: 1. Может ли верификатор байткода замедлить исполнение ? 2. Может ли верификатор байткода замедлить старт? 3. Что было бы, если бы не было верификатора байткода? 4. А что будет, если его отключить? 132
  • 133.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение? А исполнение чего? • Исполнение байткода метода • Исполнение горячего кода программы • Исполнение программы 133
  • 134.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение? А исполнение чего? • Исполнение байткода метода • Исполнение горячего кода программы • Исполнение программы 134
  • 135.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение? А исполнение чего? • Исполнение байткода метода • Исполнение горячего кода программы • Исполнение программы 135
  • 136.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение байткода метода? Вспомним: Верификатор работает до исполнения! Ответ: Нет! 136
  • 137.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение байткода метода? Вспомним: Верификатор работает до исполнения байткода! Ответ: Нет! 137
  • 138.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение байткода метода? Вспомним: Верификатор работает до исполнения байткода! Ответ: Нет! 138
  • 139.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение программы? 139
  • 140.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение программы? Верификатор работает во время загрузки класса, загрузка классов работает во время исполнения программы 140
  • 141.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение программы? Верификатор работает во время загрузки класса, загрузка классов работает во время исполнения программы Ответ: вообще говоря, да 141
  • 142.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение горячего кода? 142
  • 143.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение горячего кода? Во время исполнения горячего кода, все классы, с которым он работает, были загружены пока код был холодным. 143
  • 144.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить исполнение горячего кода? Во время исполнения горячего кода, все классы, с которым он работает, были загружены пока код был холодным. Ответ: нет 144
  • 145.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить старт программы? 145
  • 146.
    Верификатор и времястарта 0 2 4 6 8 10 12 14 16 18 20 Eclipse 4.6 Cold Start Eclipse 4.6 Warm Start verify on verify:none 146 Время старта Eclipse 4.6 в секундах
  • 147.
    Верификатор и времястарта 0 10 20 30 40 50 60 70 80 90 IntellIj IDEA 2016.3 Cold Start IntellIj IDEA 2016.3 Warm Start verify on verify:none 147 Время старта Intellij IDEA 2016.3 в секундах
  • 148.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить старт программы? 148
  • 149.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить старт программы? Ответ: незначительно 149
  • 150.
    Роль верификатора байткодав JVM Вопрос: Может ли верификатор байткода замедлить старт программы? Ответ: незначительно У JVM c AOT компилятором верификация на старте может не происходить 150
  • 151.
    Роль верификатора байткодав JVM Вопрос: Что было бы, если бы не было верификатора? 151
  • 152.
    Роль верификатора байткодав JVM Вопрос: Что было бы, если бы не было верификатора? Ответ: пришлось бы вставлять проверки во время исполнения: 152
  • 153.
    Роль верификатора байткодав JVM Вопрос: Что было бы, если бы не было верификатора? Ответ: пришлось бы вставлять проверки во время исполнения: • для инструкций работающих со стеком, проверять что стек не пуст и не переполнится 153
  • 154.
    Роль верификатора байткодав JVM Вопрос: Что было бы, если бы не было верификатора? Ответ: пришлось бы вставлять проверки во время исполнения: • для инструкций работающих со стеком, проверять что стек не пуст и не переполнится • для того, чтобы сложить два числа проверять, что на стеке лежат два числа 154
  • 155.
    Роль верификатора байткодав JVM Вопрос: Что было бы, если бы не было верификатора? Ответ: пришлось бы вставлять проверки во время исполнения: • для инструкций работающих со стеком, проверять что стек не пуст и не переполнится • для того, чтобы сложить два числа проверять, что на стеке лежат два числа Как бы быстро тогда работали Java программы? 155
  • 156.
    Роль верификатора байткодав JVM Факт: Интерпретатор не проверяет выходы за пределы стека операндов и типовую совместимость: если надо сложить два числа, он просто берет их со стека и складывает. 156
  • 157.
    Роль верификатора байткодав JVM Факт: Интерпретатор не проверяет выходы за пределы стека операндов и типовую совместимость: если надо сложить два числа, он просто берет их со стека и складывает. Факт: JIT компилятор при генерации машинного кода тоже не вставляет такие проверки в машинный код. 157
  • 158.
    Роль верификатора байткодав JVM Факт: Интерпретатор не проверяет выходы за пределы стека операндов и типовую совместимость: если надо сложить два числа, он просто берет их со стека и складывает. Факт: JIT компилятор при генерации машинного кода тоже не вставляет такие проверки в машинный код. Вывод: Наличие верификатора – первопричина почему JVM исполняет Java программы быстро! 158
  • 159.
    Роль верификатора байткодав JVM Вопрос: А что будет, если верификатор отключить? # A fatal error has been detected by the Java Runtime Environment: Exception in thread "main" java.lang.StackOverflowError at test.foo(test.j) at test.main(test.j:3) # # Internal Error (javaCalls.cpp:53), pid=8012, tid=16396 # guarantee(!thread->is_Compiler_thread()) failed: cannot make java calls from the compiler # 159
  • 160.
    Роль верификатора байткодав JVM Вопрос: А что будет, если верификатор отключить? # A fatal error has been detected by the Java Runtime Environment: Exception in thread "main" java.lang.StackOverflowError at test.foo(test.j) at test.main(test.j:3) # # Internal Error (javaCalls.cpp:53), pid=8012, tid=16396 # guarantee(!thread->is_Compiler_thread()) failed: cannot make java calls from the compiler # # If you would like to submit a bug report, please visit: # http://bugreport.java.com/bugreport/crash.jsp 160
  • 161.
    Роль верификатора байткодав JVM Вопрос: А что будет, если верификатор отключить? Ответ: Отключение верификатора может приводить к развалам JVM, что в свою очередь открывает потенциальные дыры в безопасности. 161
  • 162.
    Роль верификатора байткодав JVM Вопрос: А что будет, если верификатор отключить? Ответ: Отключение верификатора может приводить к развалам JVM, что в свою очередь открывает потенциальные дыры в безопасности. Вывод: Наличие верификатора делает исполнение байткода не только эффективным, но и безопасным! 162
  • 163.
    Верификатор и библиотекигенерации байткода
  • 164.
    Библиотеки генерации байткода •Низкоуровневые – ASM – BCEL • Высокоуровневые – Javassist – cglib – ByteBuddy 164
  • 165.
    ASM библиотека ASM библиотека •Позволяет полностью контролировать содержимое выходных классов, байткода 165
  • 166.
    ASM библиотека ASM библиотека •Позволяет полностью контролировать содержимое выходных классов, байткода • Как следствие низкоуровневая: вы работаете непосредственно с инструкциями байткода 166
  • 167.
    ASM библиотека ASM: примеркода MethodVisitor methodVisitor = … ; methodVisitor.visitVarInsn(Opcodes.ILOAD, 3); methodVisitor.visitIntInsn (Opcodes.BIPUSH, 5); methodVisitor.visitInsn (Opcodes.IADD); methodVisitor.visitVarInsn(Opcodes.ISTORE, 4); 167
  • 168.
    ASM библиотека • Нужнознать семантику байткодных инструкций • Легко породить неверифицируемый байткод • ASM умеет вычислять StackMapTable атрибут – с помощью того же потокового анализа • Можно задавать StackMapTable атрибут руками – для скорости; автоматическое вычисление замедляет ASM в 2 р. 168
  • 169.
    ASM библиотека • Нужнознать семантику байткодных инструкций • Легко породить неверифицируемый байткод • ASM умеет вычислять StackMapTable атрибут – с помощью того же потокового анализа • Можно задавать StackMapTable атрибут руками – для скорости; автоматическое вычисление замедляет ASM в 2 р. 169
  • 170.
    ASM библиотека • Нужнознать семантику байткодных инструкций • Легко породить неверифицируемый байткод • ASM умеет вычислять StackMapTable атрибут – с помощью того же потокового анализа • Можно задавать StackMapTable атрибут руками – для скорости; автоматическое вычисление замедляет ASM в 2 р. 170
  • 171.
    ASM библиотека • Нужнознать семантику байткодных инструкций • Легко породить неверифицируемый байткод • ASM умеет вычислять StackMapTable атрибут – с помощью того же потокового анализа • Можно задавать StackMapTable атрибут руками – для скорости; автоматическое вычисление замедляет ASM в 2 раза 171
  • 172.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеют вычислять StackMapFramе 172
  • 173.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеют вычислять StackMapFramе В результате вы можете получить ошибку: java.lang.VerifyError: Expecting a stackmap frame at branch 173
  • 174.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеют вычислять StackMapFramе Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier 174
  • 175.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеют вычислять StackMapFramе Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier Тогда будет работать Type Inference Verifier 175
  • 176.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеют вычислять StackMapFramе Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier Тогда будет работать Type Inference Verifier – Развал JVM не случится 176
  • 177.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеет вычислять StackMapFramе Можно отключить Typе Checking Verifier: -XX:-UseSplitVerifier Тогда будет работать Type Inference Verifier – Но старый верификатор не знает про invokedynamic – Случится VerifyError при использовании лямбд 177
  • 178.
    Библиотеки генерации байткода Проблема:некоторые (старые) библиотеки, манипулирующие байткодом, не умеет вычислять StackMapFramе Решение: не использовать старые библиотеки. Используя ASM, вычислять StackMapFramе самим или с помощью ASM. 178
  • 179.
  • 180.
    Заключение Верификатор Java байткода: •Незаслуженно обделенная вниманием часть JVM • Первопричина почему JVM исполняет программы быстро (практически на уровне низкоуровневых языков) • Гарантирует безопасность исполнения • Отключать верификатор не имеет никакого практического cмысла • Зная как работает верификатор, порождать байткод легко 180
  • 181.
    Заключение Верификатор Java байткода: •Незаслуженно обделенная вниманием часть JVM • Первопричина почему JVM исполняет программы быстро (практически на уровне низкоуровневых языков) • Гарантирует безопасность исполнения • Отключать верификатор не имеет никакого практического cмысла • Зная как работает верификатор, порождать байткод легко 181
  • 182.
    Заключение Верификатор Java байткода: •Незаслуженно обделенная вниманием часть JVM • Первопричина почему JVM исполняет программы быстро (практически на уровне низкоуровневых языков) • Гарантирует безопасность исполнения • Отключать верификатор не имеет никакого практического cмысла • Зная как работает верификатор, порождать байткод легко 182
  • 183.
    Заключение Верификатор Java байткода: •Незаслуженно обделенная вниманием часть JVM • Первопричина почему JVM исполняет программы быстро (практически на уровне низкоуровневых языков) • Гарантирует безопасность исполнения • Отключать верификатор не имеет никакого практического cмысла • Зная как работает верификатор, порождать байткод легко 183
  • 184.
    Заключение Верификатор Java байткода: •Незаслуженно обделенная вниманием часть JVM • Первопричина почему JVM исполняет программы быстро (практически на уровне низкоуровневых языков) • Гарантирует безопасность исполнения • Отключать верификатор не имеет никакого практического cмысла • Зная как работает верификатор, порождать байткод легко 184
  • 185.
    Заключение Верификатор Java байткода: •Незаслуженно обделенная вниманием часть JVM • Первопричина почему JVM исполняет программы быстро (практически на уровне низкоуровневых языков) • Гарантирует безопасность исполнения • Отключать верификатор не имеет никакого практического cмысла • Зная как работает верификатор, порождать байткод легко 185
  • 186.
    Вопросы и ответы НикитаЛипский, Excelsior nlipsky@excelsior-usa.com twitter: @pjBooms 186