Tópicos de JVM

Propaganda
Compiladores
Tópicos de JVM
Rui Gustavo Crespo
IST/DEEC@2005
Compiladores
JVM : 1/40
Máquinas virtuais (1)
• Um programa, codificado numa linguagem de alto
nível, só pode ser executado na máquina para a
qual o compilador gera código final
– Vantagens: maior eficiência no desempenho (memória
e tempo de execução)
– Inconvenientes: execução noutra máquina exige
• Nova compilação
• Duplicação de espaço em disco (ficheiros executáveis para
cada máquina)
• Selecção entre as diversas configurações do programa
executável.
Compiladores
JVM : 2/40
Máquinas virtuais (2)
• A compilação para máquina virtual, permite haver um
único programa executável para máquinas distintas.
• A execução é feita por um interpretador específico, que
transcreve instruções da máquina virtual para o
processador+sistema operativo.
Programa fonte
Compilador
Executável
Compiladores
Máquina
virtual
Processador + SO
Saída
JVM : 3/40
Máquinas virtuais (3)
• A máquina virtual de eleição é a pilha
– Vantagens:
• Transcrição muito fácil
• Máquinas reais disponibilizam instruções para manipulação de pilhas
– Inconvenientes: processamento muito lento!
• Exemplos de máquinas virtuais baseadas em pilhas
– Compilador UCSD Pascal gera código para máquina baseada numa
pilha,o P-code.
– JVM-”Java Virtual Machine”, da Sun, para Java
– CLI-”Common Language Infrastructure”, da Microsoft, para C# e
VisualBasic
– Linguagem Postscript processada por ferramentas (ex: Gview) com
4 pilhas: operando, dicionário, execução e gráfico
Compiladores
JVM : 4/40
Máquinas virtuais (4)
• O JVM foi implementado em diversas plataformas
– SPARC, de sistema operativo Sun-OS
– Intel 86, de sistemas operativos Windows e Linux
– powerPC, de sistema operativo MacOS
• JVM executa “threads”
– “thread”, ou “lighweight process”, é uma unidade de
processamento paralelo dentro do mesmo programa.
– ao contrário dos processos, todas as “threads” são
executadas dentro do mesmo ambiente)
Compiladores
JVM : 5/40
JVM – arquitectura (1)
A. Mecanismos do JVM:
–
Carregamento de classes (“class loader”): mecanismo
que instala classes e interfaces.
O mecanismo inclui verificações de segurança (ex: o
ficheiro .class está bem definido).
–
Engenho de execução (“execution engine”):
mecanismo responsável pela execução dos métodos
das classes carregadas.
A informação necessária à execução é dividida em 4
espaços conceptuais:
Compiladores
JVM : 6/40
JVM – arquitectura (2)
Engenho de execução do JVM
optop
…
opval_2
opval_1
pc
Bytecodes
Código
Activação
Pilha operandos
Ambiente
execução
lvar_n
Dados
da classe
Armazém
constantes
Área de
método
Compiladores
…
Variáveis locais
lvar_1
lvar_0
Pilha Java
JVM : 7/40
JVM – arquitectura (3)
B. Espaços conceptuais da máquina de execução
1.
Área do método (“method area”), partilhado por todas as
“threads” onde reside:
•
•
•
2.
código de cada método da classe
dados da classe
armazém de constantes (“constant pool”) para literais de tipos
primitivos e instâncias da classe de caracteres
Activações (“frames”), uma para cada evocação do método
–
–
Compiladores
Acervo (“heap”), de onde são é obtida área para os dados das
instâncias
Registo contador de programa (“program counter”), um por cada
“thread”, com o endereço da instrução a executar.
JVM : 8/40
JVM – arquitectura (4)
C. Componentes de activação de método:
1.
Pilha privada para
–
–
2.
Avaliação de expressões
Controlo do fluxo de execução do programa
Tabela de 64K para variáveis locais, referenciadas por inteiros
–
–
–
0 referencia objecto que executa o método (this)
Parâmetros da evocação referenciados a partir de 1
Variáveis locais ocupam restantes posições
Nota: nas classes static, parâmetros são referenciados a partir de 0.
Nota: O JVM tem uma pilha, onde são inseridas as plihas privadas de
cada método evocado. A activação no topo da pilha JVM designada
por pilha activa de activação (a única activação que pode ser usada).
Compiladores
JVM : 9/40
Instruções - formato
•
•
•
Instruções designadas por Bytecodes, por o
código ocupar apenas 1 Byte (200 ao todo)
Cada instrução pode ter 0, ou mais, operandos.
As instruções são carregadas na área de método
do JVM, e executadas quando o método (que as
gera) é evocado.
Compiladores
JVM : 10/40
Instruções – tipos (1)
O tipo de dados envolvido na instrução é
determinado por uma letra.
Letra
Tipo
Letra
Tipo
b
Byte
L
Instância de classe
s
Short
[
Tabela 1 dimensão
c
Caractere
V
“void”
i
Inteiro
a
Referência
z
Booleano
l
long
d
Double
Compiladores
Lnome;
Ex: Ljava/lang/String; é de
tipo cadeia de caracteres
JVM : 11/40
Instruções – tipos (2)
•
•
•
JVM segue representação “big-endien” (Byte mais significativo no
endereço mais baixo)
Os tipos primitivos b,s,c,i e f ocupam uma entrada na pilha. Os tipos
primitivos l e d ocupam duas entradas na pilha.
Número de Bytes dos literais de cada tipo usados na instrução:
Compiladores
Tipo
Espaço (bytes)
Tipo
Espaço (bytes)
b
1
l
8 (2 entradas na pilha)
s
2
f
4
c
2
d
8 (2 entradas na pilha)
i
4
JVM : 12/40
Instruções –tipos (3)
– i2[bcsfld] converte inteiro no topo da pilha
Ex: i2f, converto inteiro para float
– f2[ild], d2[ifl]
Nota: não é “cast”, pode haver perda de informação!
Compiladores
JVM : 13/40
Instruções – “load”/ “store” (1)
– Tload n
carrega na pilha valor T na posição n
Pilha Tabela variáveis
x
0 1 2 3
a b c d
Pilha Tabela variáveis
iload 2
⇒
c
x
0 1 2 3
a b c d
– Tload_[0-3]carrega na pilha valor T nas posições
entre 0 e 3 (instrução sem operandos)
Ex: iload_2, carrega inteiro da posição 2 mas ocupa
apenas 1 Byte
Compiladores
JVM : 14/40
Instruções – “load”/ “store” (2)
– bipush val
– sipush val
– iconst_m1
– aconst_null
– Tconst_vT
Tipo
vT
Tipo
vT
i
0..5
f
0..2
l
0..1
carrega na pilha o byte val
carrega na pilha o short val
carrega na pilha inteiro –1
(instrução sem operandos)
carrega na pilha null
carrega na pilha literal
(instrução sem operandos)
d
0..1
Pilha Tabela variáveis
x
Compiladores
0 1 2 3 iconst_2
⇒
a b c d
Pilha Tabela variáveis
2
x
0 1 2 3
a b c d
JVM : 15/40
Instruções – “load”/ “store” (3)
– ldc #idx
– ldc_w #idx
– ldc2_w #idx
– Tstore n
carrega constante de índice idx (8 bits), do
armazém de constantes
carrega constante de índice idx,idx+1 (16 bits)
do armazém de constantes
carrega constante l/d de índice idx,idx+1 do
armazém de constantes
salva da pilha valor T na posição n
Pilha Tabela variáveis
y
x
0 1 2 3 istore 2
⇒
a b c d
Pilha Tabela variáveis
x
0 1 2 3
a b y d
– Tstore_[0-3]
Compiladores
JVM : 16/40
Instruções – “load”/ “store” (4)
– getstatic
– getfield
carrega referência a variável de classe
carrega referência a variável de
instância
Exemplo:
getstatic java/lang/System.in Ljava/io/InputStream;
campo
Compiladores
JVM : 17/40
Instruções – aritméticas (1)
– Tadd
retira 2 operandos da pilha, insere soma na
pilha
Pilha Tabela variáveis
y
x
0 1 2 3
a b c d
Pilha Tabela variáveis
iadd
⇒
y+x
0 1 2 3
a b c d
– Tsub, Tmul, Tdiv, Tneg, Trem
T é de tipo i,l, f, d
resto
negação
– iinc pos val
adiciona val na posição pos
(pode ser negativo)
Compiladores
JVM : 18/40
Instruções – aritméticas (2)
– Tshl,Tshr
– Tand
– Tor
– Txor
Compiladores
desloca operando no 2º lugar, pelos
bits mais baixos no oper do topo.
T=i (5 bits), ou T=l (6 bits)
And bit a bit
Or bit a bit
Xor bit a bit
JVM : 19/40
Instruções – pilha operandos (1)
retira topo da pilha
– pop
Pilha Tabela variáveis
y
x
0 1 2 3
a b c d
– pop2
– swap
Compiladores
pop
⇒
x
0 1 2 3
a b c d
retira duas posições da pilha
troca dois operandos
Pilha Tabela variáveis
y
x
Pilha Tabela variáveis
0 1 2 3
a b c d
Pilha Tabela variáveis
swap
⇒
x
y
0 1 2 3
a b c d
JVM : 20/40
Instruções – pilha operandos (2)
– dup
duplica topo da pilha
Pilha Tabela variáveis
Pilha Tabela variáveis
x
0 1 2 3
a b c d
dup
⇒
x
x
0 1 2 3
a b c d
Nota: tipicamente, a geração de um objecto feita por uma
sequência de instruções new e evocação do <init>
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
Compiladores
JVM : 21/40
Instruções - comparações
– Tcmpg retira 2 operandos da pilha (T:d ou f),
compara-os e insere inteiro na pilha
» 0 : iguais
» 1 : topo<2º, ou um dos valores igual a NaN
» -1 : topo>2º
– Tcmpl semelhante a Tcmpg, excepto resultado –1 se
um dos valores for NaN.
Compiladores
JVM : 22/40
Instruções – controlo execução (1)
salto incondicional (16 bits)
salto incondicional (32 bits)
retira topo da pilha, compara-o XX com zero,
salta se resultado for verdadeiro
– if_icmpXX <lbl>
retira dois elementos, compara-os XX,
salta se resultado for verdadeiro
– jmp <lbl>
– jmp_w <lbl>
– ifXX <lbl>
As etiquetas
são da forma
id:
inst
Compiladores
XX
Condição
XX
Condição
eq
val == 0
gt
val > 0
lt
val < 0
ge
val >= 0
le
val <= 0
null
val == null
ne
val!= 0
nonnull
val!= null
JVM : 23/40
Instruções – controlo execução (2)
– lookupswitch
<chave1>: <lbl1>
<chave2>: <lbl1>
…
default: <def-lbl>
Compiladores
Retira topo da pilha e salta
para etiqueta indicada pelo
valor
JVM : 24/40
Instruções – objectos/métodos (1)
– invokeMode <method-spec>
Existem 4 modos de invocação:
• interface (método declarado numa interface)
• special (métodos que requerem manipuladores especiais:
<init> ou <super>)
• static (método de classe estática)
• virtual (método normal)
Nota: nas versões anteriores ao JDK1.02, instrução
invokespecial denominada invokenonvirtual
Compiladores
JVM : 25/40
Instruções – objectos/métodos (2)
• <method-spec> possui formato
topo
nome-classe/método/(argumentos)tipo-retorno
Pilha antes da chamada:
Obj-ref arg1 arg2 … argN
Pilha no retorno:
[res]
• Os argumentos são sequência de tipos, sem separadores.
Ex: private static void quicksort(char[] buffer, int lower,
int upper) transcrito por
.method public static quicksort([CII)V
Nota: na invocação é obrigatório indicar os tipos de
parâmetros, devido ao polimorfismo do JVM
Compiladores
JVM : 26/40
Instruções – objectos/métodos (3)
– Treturn
retorna valor no topo da pilha (T=“”, se método
retornar void)
cria instância de classe e insere referência
no topo da pilha
Instrução new deve ser seguida pela evocação da
inicialização, que consome da pilha a referência.
Exemplo: new Integer(20); transcrito por
– new
new java/lang/Integer
; cria objecto
dup
; duplica topo pilha
bipush 20
; insere valor
invokespecial java/lang/Integer/<init>(i)V
Compiladores
parâmetro de init
JVM : 27/40
Instruções – tabelas (1)
São objectos, mas tratados com instruções
apropriadas para tornar código mais eficiente
– newarray Tipo
cria tabela
Ex: newarray int (tabela de inteiros)
– multianewarray <desc-array> <num-dim>
Nota: 1<num-dim<255
Ex: new int[6][3][] transcrita por
bipush 6
bipush 3
multianewarray [[[I 2 ;aloca 2 das 3 dimensões
Compiladores
JVM : 28/40
Instruções – tabelas (2)
insere na pilha componente da tabela,
de tipo T.
– Tastore
retira da pilha topo, de tipo T e
modifica componente da tabela.
– arraylength
obtém dimensão da tabela
– Taload
Compiladores
JVM : 29/40
Instruções – tabelas (3)
; x = new int[6][3][];
bipush 6
bipush 3
multianewarray [[[I 2
astore_1
; guarda referência em x
; x[0][1] = new int[50];
aload_1
; insere na pilha referência da tabela
iconst_0
; insere na pilha x[0]
aaload
;
iconst_1
; aloca tabela de 50 inteiros
bipush 50
;
newarray int ;
aastore
; guarda referência em x[0][1]
Compiladores
JVM : 30/40
Instruções – excepções (1)
• As excepções são lançadas pela instrução athrow.
O topo da pilha deve conter a referência ao objecto
instância de uma excepção.
new java/io/IOException
dup
invokespecial java/io/IOException/<init>()V
athrow
Compiladores
JVM : 31/40
Instruções – excepções (2)
• O corpo try{…}catch{…} é determinado no
jasmin pela directiva
.catch java/lang/Exception from lb11 to lbl2 using Handler
lbl1 e lbl2 são as etiquetas das instruções
delimitadoras do try{…}, e Handler é a
etiqueta da primeira instrução do catch{…}.
Compiladores
JVM : 32/40
Directivas (1)
• A classe pode conter as directivas
– .source id
# opcional
– .class Qualif id
Qualif é o qualificador da classe (public,…)
– .super id
– .implements id
Exemplo
.source HelloWorld.j
.class public HelloWorld
.super java/lang/Object
Compiladores
JVM : 33/40
Directivas (1)
• O método deve ser delimitado pelas directivas
– .method Qualif id(Pars)Tipo
Qualif é o qualificador do método (public,…)
– .end method
• Em cada método devem ser indicadas as seguintes
directivas
– .limits locals nn
– .limits stack nn
Compiladores
espaço de variáveis
locais (this ocupa 1ª pos.
nos métodos não estáticos)
profundidade máxima da
pilha de operandos
JVM : 34/40
Armazém de constantes
• Constantes armazenadas no armazém “constant
pool”, indexadas a partir de #1.
void useManyNumeric() {
int i = 100;
int j = 1000000;
long l1 = 1;
long l2 = 0xffffffff;
double d = 2.2;
...
}
Compiladores
Method void useManyNumeric()
0 bipush 100 // Push a small int with bipush
2 istore_1
3 ldc #1 // Push int constant 1000000; a larger int
// value uses ldc
5 istore_2
6 lconst_1 // A tiny long value uses short, fast lconst_1
7 lstore_3
8 ldc2_w #6 // Push long 0xffffffff (that is, an int -1); any
// long constant value can be pushed using ldc2_w
11 lstore 5
13 ldc2_w #8 // Push double constant 2.200000; uncommon
// double values are also pushed using ldc2_w
16 dstore 7
JVM : 35/40
Exemplo (1)
Ex: O classico Hello world!
class HelloWorld {
public static void main(String args[]) {
System.out.println(“Hello World!"); }
}
.source HelloWorld.j
.class public HelloWorld
.super java/lang/Object
.method public <init>()V ; construtor simples
aload_0
invokenonvirtual java/lang/Object/<init>()V
return
.end method
Compiladores
JVM : 36/40
Exemplo (2)
.method public static main([Ljava/lang/String;)V
.limit stack 2
getstatic java/lang/System/out:Ljava/io/PrintStream;
ldc "Hello World!"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
Nota: o ficheiro .class ocupa apenas 377 B
Compiladores
JVM : 37/40
Ferramentas (1)
• javap – c lista código JVM de ficheiro class
Exemplo
public class Test {
public static void main(String args[]) {
int i;
i = 2;
i = i + 7; }
}
Compiladores
JVM : 38/40
Ferramentas (2)
C:\ > javap -c Test
Compiled from Test.java
public class Test extends java.lang.Object {
public Test(); // a default constructor created
public static void main(java.lang.String[]);
}
Method Test()
0 aload_0
1 invokespecial #1
4 return
Method void main(java.lang.String[])
0 iconst_2 // Put integer 2 on stack
1 istore_1 // Store the top stack value at location 1
2 iload_1 // Put the value at location 1 on stack
3 bipush 7 // Put the value 7 on the stack
5 iadd
// Add two top stack values together
6 istore_1 // The sum, on top of stack, stored at location 1
7 return
// Finished processing
Compiladores
JVM : 39/40
Ferramentas (3)
• jasmin – gera ficheiro .class
Pode ser obtido na página
http://jasmin.sourceforge.net
$ java -jar jasmin.jar <filenames>
Compiladores
JVM : 40/40
Download