Wednesday, 22 July 2009

Java: finding class versions

The JVM versions your Java classes will run on is often determined by how you compile them. Failure to take care with your classes and dependencies can lead to an UnsupportedClassVersionError. This post demonstrates how to check your class files.

The class file format

Information on the Java binary format can be found in The Java Virtual Machine Specification. The ClassFile Structure defines the format and specifies that the version will be contained in the first eight bytes.

    ClassFile {
      u4 magic;
      u2 minor_version;
      u2 major_version;

   ...remainder elided...

    }

This can be seen by doing a hex dump of a .class file.

magic minor_version major_version
CA FE BA BE 00 00 00 32
The first eight bytes of a class compiled with Java 1.6

The class file in the table has a major version of 0x32 and a minor version of 0x0. The decimal value (50.0) matches the version stored in the JVM's system properties:

    System.out.println(System
        .getProperty("java.class.version"));

Targeting old Java versions with newer compilers

Java compilers let you target older class versions to run on older VMs.

public class Java {}

The above Java file can be compiled to various class file versions using the Java 6 javac JDK compiler:

javac -source 1.3 -target 1.1 Java.java
javac -source 1.3 -target 1.2 Java.java
javac -source 1.3 -target 1.3 Java.java
javac -source 1.3 -target 1.4 Java.java
javac -source 1.3 -target 1.5 Java.java
javac -source 1.3 -target 1.6 Java.java

Note that the class versions are different to the Java libraries that are available in older JVMs. If you target an older JVM, you need to restrict your API calls to the ones available in that version.

Reading the Java versions

The following ClassFile versions are supported by various JVMs:

Maximum Class File Version Java Version Source
45.3 1.0.2 documentation
45.65535 1.1.X documentation
46.0 1.2 documentation
47.0 1.3 experimentation
48.0 1.4 experimentation
49.0 5 experimentation
50.0 6 experimentation

I can't be 100% certain about the values in the table determined by experimentation - if you can find canonical information from Sun, please drop a link into a comment.

This code reads the start of a class binary file from a stream to find the major and minor version numbers:

    DataInputStream data = new DataInputStream(in);
    int magic = data.readInt();
    if (magic != 0xCAFEBABE) {
      throw new IOException("Invalid Java class");
    }
    int minor = 0xFFFF & data.readShort();
    int major = 0xFFFF & data.readShort();

Sample Code

All the sources are available in a public Subversion repository.

Repository: http://illegalargumentexception.googlecode.com/svn/trunk/code/java/
License: MIT
Project: JavaClassVersionLib

You can download a binary version here: javaClassVersionLib_1.1.zip. (download updated 26 Nov '09)

1 comment:

All comments are moderated