2012-01-21 14 views
26

La instrucción invokespecial JVM se utiliza para llamar a métodos de inicialización (<init>) al crear objetos nuevos. La descripción de la instrucción sugiere (pero no aclara) que la decisión de llamar al constructor de una superclase o un constructor de la clase actual depende del estado del indicador ACC_SUPER establecido en el archivo class.¿Cuál es el propósito del indicador de acceso ACC_SUPER en los archivos de la clase Java?

partir de la especificación JVM de Sun:

A continuación, se selecciona el método de resolver para la invocación a menos que todas las condiciones siguientes:

  • La bandera ACC_SUPER (véase la Tabla 4.1, "Clase modificadores de acceso y propiedad ") se establece para la clase actual.

- Source (definición invokespecial código de operación)

El ajuste de la bandera ACC_SUPER indica cuál de las dos alternativas para la semántica de su instrucción invokespecial la máquina virtual de Java es expresar; el indicador ACC_SUPER existe para la compatibilidad con versiones anteriores del código compilado por los compiladores más antiguos de Sun para el lenguaje de programación Java. Todas las implementaciones nuevas de la máquina virtual Java deben implementar la semántica para invokespecial documentada en esta especificación. Todos los nuevos compiladores del conjunto de instrucciones de la máquina virtual Java deben establecer el indicador ACC_SUPER. Los compiladores antiguos de Sun generaron indicadores de ClassFile con ACC_SUPER unset. Las implementaciones anteriores de la máquina virtual Java de Sun ignoran el indicador si está configurado.

- Source (ClassFile formato)

La definición establece que la bandera es para mantener la compatibilidad con viejos compiladores. Sin embargo, sigue en contradicción con Sun's older Java virtual machine implementations ignore the flag if it is set.

¿La bandera todavía se utiliza con el código de operación invokespecial? Por lo que puedo decir, parece no tener ningún propósito y no puedo encontrar un recurso para sugerir que alguna vez lo hizo.

Gracias.

+1

Esto no es una respuesta, pero para entender por qué la bandera se utiliza Creo que también hay que entender lo que significaba la bandera bajo la especificación JVM versión 1 - ver http://java.sun.com/docs/books /jvms/first_edition/html/Instructions2.doc7.html#invokespecial –

+0

Al lado del otro, no puedo ver ninguna diferencia. – Jivings

Respuesta

33

ACC_SUPER se introdujo para corregir un problema con la invocación de supermétodos. El indicador ACC_SUPER marca una clase como compilada para la semántica modificada de la instrucción de código de operación 183. Su propósito es similar al del número de versión del archivo de clase, ya que permite a la JVM detectar si se compiló una clase para la semántica más antigua o más reciente de esa instrucción. Java 1.0.2 no configuró e ignoró ACC_SUPER mientras que Java 1.1 y posteriores siempre configuran ACC_SUPER.

Antes de Java 1.1, la instrucción de código de bytes con el código de operación 183 que ahora se llama invokespecial se llamaba invokenonvirtual y tenía una especificación parcialmente diferente. Se usaba siempre que los métodos de instancia tenían que invocarse sin una búsqueda de método virtual. Este fue el caso de métodos privados, inicializadores de instancias (constructores) e implementar invocaciones de métodos en super. Pero el último caso causó problemas con las bibliotecas de clase en evolución.

Una referencia de método en el código de bytes (CONSTANT_Methodref_info) no solo define el nombre y el argumento y los tipos de retorno de un método, sino también la clase a la que pertenece.El Opcode 183 obtiene un parámetro de referencia de método e intenta invocar directamente el método al que se hace referencia de la clase especificada sin más búsquedas. En el caso de las invocaciones en super, fue responsabilidad de los compiladores resolver la superclase más cercana que implementa este método y generar una referencia al código de bytes.

Desde Java 1.1 se cambió para ignorar esencialmente la clase a la que se hace referencia en CONSTANT_Methodref_info y buscar en su lugar el supermétodo más cercano con el nombre del método y la firma en la JVM. Esto generalmente se hace ahora cuando la clase se carga o justo antes de que se ejecute la instrucción o se compile JIT la primera vez.

Aquí es un ejemplo por qué este cambio era necesario. En Java 1.0.2 del contenedor de clases AWT y componentes se define de esta manera:

class Component 
{ 
    public void paint(Graphics g) {} 
} 

class Container extends Component 
{ 
    // inherits paint from Component but doesn't override it 
} 

en Java 1.1 se cambió a la clase conatiner a tener su propia implementación de paint:

class Container extends Component 
{ 
    public void paint(Graphics g) {/*...*/} 
} 

Ahora, cuando usted tenía una subclase directa o indirecta de los recipientes que hizo una llamada en super.paint(g) y compilado por 1.0.2 se genera una instrucción para invokenonvirtualComponent.paint ya que este fue el primer padre que tenía este método. Pero si utilizó esta clase compilada en una JVM que también tenía Container.paint, igual habría llamado Component.paint, que no es lo que usted esperaría.

Por otro lado, cuando compiló la clase para 1.1 y la ejecutó en una JVM 1.0.2 arrojaría un AbstractMethodError o más probablemente para máquinas virtuales de esa época simplemente se bloquee. Para evitar el bloqueo, tenía que escribir ((Component)super).paint(g) y compilarlo con un compilador de 1.1 para obtener el comportamiento deseado en cualquiera de las VM. Esto establecería ACC_SUPER pero aún generaría la instrucción para llamar al Component.paint. Una máquina virtual 1.0.2 ignoraría ACC_SUPER e ir directamente a invocar Component.paint que está muy bien, mientras que un 1,1 VM encontraría ajustado ACC_SUPER y por lo tanto hacer la propia consulta de lo que haría invocar Container.paint a pesar de que la referencia al método de código de bytes era Component.paint.

Puede encontrar más información sobre esto en this old post on the ikvm.net weblog.

+0

Publicación interesante. Entonces, ¿todos los compiladores después de 1.1 establecen la bandera, haciendo que la JVM verifique la jerarquía 'super' al resolver los métodos? ¿Eso está hecho en lugar de usar el 'objectref' sacado de la pila? – Jivings

+0

Esta es una muy buena respuesta. Ahora entiendo cómo funciona 'invokespecial' con la bandera. Pero ¿por qué no solo desaprobaron el método anterior? – Jivings

+13

A partir de la actualización de seguridad de Java 7u13, la JVM ya no respeta la ausencia del indicador ACC_SUPER. Esto se debe al problema que se describe aquí: http://weblog.ikvm.net/PermaLink.aspx?guid=23cced47-ccdb-460d-acc9-ce16154ab6a5 –

Cuestiones relacionadas