Existe alguma sobrecarga quando lançamos objetos de um tipo para outro? Ou o compilador simplesmente resolve tudo e não há custo em tempo de execução?
São coisas gerais ou há casos diferentes?
Por exemplo, suponha que temos um array de Object [], onde cada elemento pode ter um tipo diferente. Mas sempre sabemos com certeza que, digamos, o elemento 0 é um Double, o elemento 1 é uma String. (Eu sei que este é um design errado, mas vamos supor que tive que fazer isso.)
As informações de tipo do Java ainda são mantidas em tempo de execução? Ou tudo é esquecido após a compilação, e se fizermos (Double) elementos [0], vamos apenas seguir o ponteiro e interpretar esses 8 bytes como um double, o que quer que seja?
Não estou muito certo sobre como os tipos são feitos em Java. Se você tem alguma recomendação sobre livros ou artigos, obrigado também.
Respostas:
Existem 2 tipos de fundição:
Cast implícito , quando você converte de um tipo para um tipo mais amplo, que é feito automaticamente e não há sobrecarga:
Casting explícito , quando você passa de um tipo mais largo para um mais estreito. Para este caso, você deve usar explicitamente a transmissão como esta:
Nesse segundo caso, há sobrecarga no tempo de execução, pois os dois tipos devem ser verificados e, caso a conversão não seja viável, a JVM deve lançar uma ClassCastException.
Retirado de JavaWorld: O custo de fundição
fonte
((String)o).someMethodOfCastedClass()
Para uma implementação razoável de Java:
Cada objeto possui um cabeçalho contendo, entre outras coisas, um ponteiro para o tipo de tempo de execução (por exemplo
Double
ouString
, mas nunca poderia serCharSequence
ouAbstractList
). Assumindo que o compilador de tempo de execução (geralmente HotSpot no caso da Sun) não pode determinar o tipo estaticamente, algumas verificações precisam ser realizadas pelo código de máquina gerado.Primeiro, esse ponteiro para o tipo de tempo de execução precisa ser lido. De qualquer forma, isso é necessário para chamar um método virtual em uma situação semelhante.
Para lançar para um tipo de classe, sabe-se exatamente quantas superclasses existem até você atingir
java.lang.Object
, então o tipo pode ser lido em um deslocamento constante do ponteiro de tipo (na verdade, os primeiros oito no HotSpot). Novamente, isso é análogo a ler um ponteiro de método para um método virtual.Então, o valor lido só precisa de uma comparação com o tipo estático esperado do elenco. Dependendo da arquitetura do conjunto de instruções, outra instrução precisará ramificar (ou falhar) em uma ramificação incorreta. ISAs, como o ARM de 32 bits, têm instrução condicional e podem fazer com que o caminho triste passe pelo caminho feliz.
As interfaces são mais difíceis devido à herança múltipla da interface. Geralmente, as duas últimas conversões para interfaces são armazenadas em cache no tipo de tempo de execução. Nos primeiros dias (mais de uma década atrás), as interfaces eram um pouco lentas, mas isso não é mais relevante.
Esperançosamente, você pode ver que esse tipo de coisa é irrelevante para o desempenho. Seu código-fonte é mais importante. Em termos de desempenho, o maior acerto em seu cenário é passível de perdas de cache por perseguir ponteiros de objeto em todo o lugar (as informações de tipo serão, obviamente, comuns).
fonte
O compilador não observa os tipos dos elementos individuais de uma matriz. Ele simplesmente verifica se o tipo de cada expressão de elemento pode ser atribuído ao tipo de elemento da matriz.
Algumas informações são mantidas em tempo de execução, mas não os tipos estáticos dos elementos individuais. Você pode dizer isso observando o formato do arquivo da classe.
É teoricamente possível que o compilador JIT possa usar "análise de escape" para eliminar verificações de tipo desnecessárias em algumas atribuições. No entanto, fazer isso na medida em que você está sugerindo estaria além dos limites da otimização realista. A recompensa de analisar os tipos de elementos individuais seria muito pequena.
Além disso, as pessoas não deveriam escrever código de aplicativo assim de qualquer maneira.
fonte
(float) Math.toDegrees(theta)
Haverá uma sobrecarga significativa aqui também?A instrução de código de byte para executar a conversão em tempo de execução é chamada
checkcast
. Você pode desmontar o código Java usandojavap
para ver quais instruções são geradas.Para arrays, o Java mantém as informações de tipo em tempo de execução. Na maioria das vezes, o compilador detectará erros de tipo para você, mas há casos em que você encontrará um
ArrayStoreException
ao tentar armazenar um objeto em uma matriz, mas o tipo não corresponde (e o compilador não o detectou) . A especificação da linguagem Java fornece o seguinte exemplo:Point[] pa = cpa
é válido porqueColoredPoint
é uma subclasse de Point, maspa[0] = new Point()
não é válido.Isso se opõe aos tipos genéricos, onde não há informações de tipo mantidas em tempo de execução. O compilador insere
checkcast
instruções onde necessário.Essa diferença na digitação de tipos e arrays genéricos torna frequentemente inadequado misturar arrays e tipos genéricos.
fonte
Em teoria, há uma sobrecarga introduzida. No entanto, as JVMs modernas são inteligentes. Cada implementação é diferente, mas não é irracional supor que poderia haver uma implementação que o JIT otimizou as verificações de conversão quando poderia garantir que nunca haveria um conflito. Quanto a quais JVMs específicos oferecem isso, não sei dizer. Devo admitir que gostaria de saber os detalhes da otimização JIT, mas isso é uma preocupação dos engenheiros de JVM.
A moral da história é escrever um código compreensível primeiro. Se você estiver enfrentando lentidão, analise e identifique o seu problema. As chances são boas de que não seja devido ao elenco. Nunca sacrifique um código limpo e seguro na tentativa de otimizá-lo ATÉ QUE VOCÊ SABE QUE PRECISA.
fonte