How does bytecode get verified in the JVM?
Oracle themselves have a little snippet page on how it works here.
Basically, the JRE doesn't trust the JDK. That's because it has no knowledge of which JDK compiler created the class file. It treats the class file as hostile until verified.
Expanding on that, the bytecode verification is a necessary step to protect from what Sun call a "hostile compiler". Sun's own Java compiler ensures that Java source code doesn't violate the safety rules but, when an application imports a code fragment, it doesn't actually know if the code fragment follows Java language rules for safety. In other words, the code may not have been produced by a trustworthy Java compiler.
In that case, the Java run time system on your machine has to assume the fragment is bad and subjects it to bytecode verification.
The Java virtual machine does not even see the bytecode until it's been through this verification process. Doing this as the bytecode is loaded also has the advantage that a whole lot of run time checks don't need to be performed every time the code is executed. Because it's been verified as correct, it can, once it starts running, run faster than would otherwise be possible.
A rendition of the linked diagram is below:
<<<=== Unsafe / Safe ===>>>
\
+---------------+ +-------------------+
| Java source | +--> | Class loader | --+
+---------------+ | | Bytecode verifier | |
| | +-------------------+ |
V | / |
+---------------+ | \ V
| Java compiler | Network / +-------------------+
+---------------+ | \ | JVM/JIT |
| | / +-------------------+
V | \ |
+---------------+ | / V
| Java bytecode | --+ \ +-------------------+
+---------------+ / | Operating system |
\ +-------------------+
/ |
\ V
/ +-------------------+
\ | Hardware |
/ +-------------------+
\
<<<=== Unsafe / Safe ===>>>
The best source of information is probably the relevant section in the JVM specification, 4.10 Verification of class Files.
See the link for details, but broadly:
Linking-time verification enhances the performance of the interpreter. Expensive checks that would otherwise have to be performed to verify constraints at run time for each interpreted instruction can be eliminated. The Java Virtual Machine can assume that these checks have already been performed. For example, the Java Virtual Machine will already know the following:
- There are no operand stack overflows or underflows.
- All local variable uses and stores are valid.
- The arguments to all the Java Virtual Machine instructions are of valid types.
The verifier also performs verification that can be done without looking at the code array of the Code attribute (§4.7.3). The checks performed include the following:
- Ensuring that final classes are not subclassed and that final methods are not overridden (§5.4.5).
- Checking that every class (except
Object
) has a direct superclass.- Ensuring that the constant pool satisfies the documented static constraints; for example, that each
CONSTANT_Class_info
structure in the constant pool contains in itsname_index
item a valid constant pool index for aCONSTANT_Utf8_info
structure.- Checking that all field references and method references in the constant pool have valid names, valid classes, and a valid type descriptor.