P. 1
The Java Class File Lifestyle

The Java Class File Lifestyle

|Views: 10|Likes:
Published by Mudit Khetan

More info:

Published by: Mudit Khetan on Sep 24, 2010
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as DOC, PDF, TXT or read online from Scribd
See more
See less

01/11/2013

pdf

text

original

The Java class file lifestyle

An introduction to the basic structure and lifestyle of the Java class file
By Bill Venners, JavaWorld.com, 07/01/96 Welcome to another installment of "Under the Hood." In last month's article I discussed the Java Virtual Machine, or JVM, the abstract computer for which all Java programs are compiled. If you are unfamiliar with the JVM, you may want to read last month's article before this one. In this article I provide a glimpse into the basic structure and lifestyle of the Java class file.

Born to travel
The Java class file is a precisely defined format for compiled Java. Java source code is compiled into class files that can be loaded and executed by any JVM. The class files may travel across a network before being loaded by the JVM.

In fact, if you are reading this article via a Java-capable browser, class files for the simulation applet at the end of the article are flying across the Internet to your computer right now. If you'd like to listen in on them (and your computer has audio capability), push the following button:

Sounds like they're having fun, huh? That's in their nature. Java class files were designed to travel well. They are platform-independent, so they will be welcome in more places. They contain bytecodes, the compact instruction set for the JVM, so they can travel light. Java class files are constantly zipping through networks at breakneck speed to arrive at JVMs all over the world.

What's in a class file?
The Java class file contains everything a JVM needs to know about one Java class or interface. In their order of appearance in the class file, the major components are:

magic, version, constant pool, access flags, this class, super class, interfaces, fields, methods, and attributes. Information stored in the class file often varies in length -- that is, the actual length of the information cannot be predicted before loading the class file. For instance, the number of methods listed in the methods component can differ among class files, because it depends on the number of methods defined in the source code. Such information is organized in the class file by prefacing the actual information by its size or length. This way, when the class is being loaded by the JVM, the size of variable-length information is read first. Once the JVM knows the size, it can correctly read in the actual information.

Information is generally written to the class file with no space or padding between consecutive pieces of information; everything is aligned on byte boundaries. This helps keeps class files petite so they will be aerodynamic as they fly across networks.

The order of class file components is strictly defined so JVMs can know what to expect, and where to expect it, when loading a class file. For example, every JVM knows that the first eight bytes of a class file contain the magic and version numbers, that the constant pool starts on the ninth byte, and that the access flags follow the constant pool. But because the constant pool is variable-length, it doesn't know the exact whereabouts of the access flags until it has finished reading in the constant pool. Once it has finished reading in the constant pool, it knows the next two bytes will be the access flags.

Magic and version numbers
The first four bytes of every class file are always 0xCAFEBABE. This magic number makes Java class files easier to identify, because the odds are slim that non-class files would start with the same initial four bytes. The number is called magic because it can be pulled out of a hat by the file format designers. The only requirement is that it is not already being used by another file format that may be encountered in the real world. According to Patrick Naughton, a key member of the original Java team, the magic number was chosen "long before the name Java was ever uttered in reference to this language. We were looking for something fun, unique, and easy to remember. It is only a coincidence that OxCAFEBABE, an oblique reference to the cute baristas at Peet's Coffee, was foreshadowing for the name Java."

The second four bytes of the class file contain the major and minor version numbers. These numbers identify the version of the class file format to which a particular class file

adheres and allow JVMs to verify that the class file is loadable. Every JVM has a maximum version it can load, and JVMs will reject class files with later versions.

Constant pool
The class file stores constants associated with its class or interface in the constant pool. Some constants that may be seen frolicking in the pool are literal strings, final variable values, class names, interface names, variable names and types, and method names and signatures. A method signature is its return type and set of argument types.

The constant pool is organized as an array of variable-length elements. Each constant occupies one element in the array. Throughout the class file, constants are referred to by the integer index that indicates their position in the array. The initial constant has an index of one, the second constant has an index of two, etc. The constant pool array is preceded by its array size, so JVMs will know how many constants to expect when loading the class file.

Each element of the constant pool starts with a one-byte tag specifying the type of constant at that position in the array. Once a JVM grabs and interprets this tag, it knows what follows the tag. For example, if a tag indicates the constant is a string, the JVM expects the next two bytes to be the string length. Following this two-byte length, the JVM expects to find length number of bytes, which make up the characters of the string.

In the remainder of the article I'll sometimes refer to the nth element of the constant pool array as constant_pool[n]. This makes sense to the extent the constant pool is organized like an array, but bear in mind that these elements have different sizes and types and that the first element has an index of one.

Access flags
The first two bytes after the constant pool, the access flags, indicate whether or not this file defines a class or an interface, whether the class or interface is public or abstract, and (if it's a class and not an interface) whether the class is final.

This class
The next two bytes, the this class component, are an index into the constant pool array. The constant referred to by this class, constant_pool[this_class], has two parts, a one-byte tag and a two-byte name index. The tag will equal CONSTANT_Class, a value that indicates this element contains information about a class or interface. Constant_pool[name_index] is a string constant containing the name of the class or interface.

The this class component provides a glimpse of how the constant pool is used. This class itself is just an index into the constant pool. When a JVM looks up constant_pool[this_class], it finds an element that identifies itself as a CONSTANT_Class with its tag. The JVM knows CONSTANT_Class elements always have a two-byte index into the constant pool, called name index, following their one-byte tag. So it looks up constant_pool[name_index] to get the string containing the name of the class or interface.

Super class
Following the this class component is the super class component, another two-byte index into the constant pool. Constant_pool[super_class] is a CONSTANT_Class element that points to the name of the super class from which this class descends.

Interfaces
The interfaces component starts with a two-byte count of the number of interfaces implemented by the class (or interface) defined in the file. Immediately following is an array that contains one index into the constant pool for each interface implemented by the class. Each interface is represented by a CONSTANT_Class element in the constant pool that points to the name of the interface.

Fields
The fields component starts with a two-byte count of the number of fields in this class or interface. A field is an instance or class variable of the class or interface. Following the count is an array of variable-length structures, one for each field. Each structure reveals information about one field such as the field's name, type, and, if it is a final variable, its

constant value. Some information is contained in the structure itself, and some is contained in constant pool locations pointed to by the structure.

The only fields that appear in the list are those that were declared by the class or interface defined in the file; no fields inherited from super classes or superinterfaces appear in the list.

Methods
The methods component starts with a two-byte count of the number of methods in the class or interface. This count includes only those methods that are explicitly defined by this class, not any methods that may be inherited from superclasses. Following the method count are the methods themselves.

The structure for each method contains several pieces of information about the method, including the method descriptor (its return type and argument list), the number of stack words required for the method's local variables, the maximum number of stack words required for the method's operand stack, a table of exceptions caught by the method, the bytecode sequence, and a line number table.

Attributes
Bringing up the rear are the attributes, which give general information about the particular class or interface defined by the file. The attributes section has a two-byte count of the number of attributes, followed by the attributes themselves. For example, one attribute is the source code attribute; it reveals the name of the source file from which this class file was compiled. JVMs will silently ignore any attributes they don't recognize.

Getting loaded: a simulation of a class file reaching its JVM destination
The applet below simulates a JVM loading a class file. The class file being loaded in the simulation was generated by the javac compiler given the following Java source code:

class Act {

}

public static void doMathForever() { int i = 0; while (true) { i += 1; i *= 2; } }

The above snippet of code comes from last month's article about the JVM. It is the same doMathForever() method executed by the EternalMath applet from last month's article. I chose this code to provide a real example that wasn't too complex. Although the code may not be very useful in the real world, it does compile to a real class file, which is loaded by the simulation below.

The GettingLoaded applet allows you to drive the class load simulation one step at a time. For each step along the way you can read about the next chunk of bytes that is about to be consumed and interpreted by the JVM. Just press the "Step" button to cause the JVM to consume the next chunk. Pressing "Back" will undo the previous step, and pressing "Reset" will return the simulation to its original state, allowing you to start over from the beginning.

The JVM is shown at the bottom left consuming the stream of bytes that makes up the class file Act.class. The bytes are shown in hex streaming out of a server on the bottom right. The bytes travel right to left, between the server and the JVM, one chunk at a time. The chunk of bytes to be consumed by the JVM on the next "Step" button press are shown in red. These highlighted bytes are described in the large text area above the JVM. Any remaining bytes beyond the next chunk are shown in black.

I've tried to fully explain each chunk of bytes in the text area. There is a lot of detail, therefore, in the text area and you may wish to skim through all the steps first to get the general idea, then look back for more details. Source Code
/******************************************************************** Copyright (c) 1996 Artima Software Company. All Rights Reserved. PROJECT: MODULE: FILE: JavaWorld Under The Hood GettingLoaded.java

AUTHOR: DESCRIPTION:

Bill Venners, June 1996

This file contains all the code for the Java class load simulator that accompanies the Under The Hood article titled, "The Java Class File Lifestyle". I developed this under Symantec Cafe on Windows 95. As I developed it I had each class in its own file, which made for very speedy compile and test cycles. I lumped all the files together into this file to make it easier to download. This applet retrieves two files from the server, the Act.class file itself, from which it gets the bytes to display along the bottom, and a text file which contains the text that accompanies each step. Each block of text is separated by a line of stars which contains one star for each byte consumed by the step. *********************************************************************/ import import import import import import import import import import java.awt.*; java.applet.*; java.io.InputStream; java.io.DataInputStream; java.io.BufferedInputStream; java.io.IOException; java.io.EOFException; java.net.URL; java.net.URLConnection; java.net.MalformedURLException;

public class GettingLoaded extends Applet implements Runnable { private URL theClassFileURL; private URL theActTextURL; private Thread runner; private TextArea ta = new TextArea(); private StepNode firstNode; private StepNode lastNode; private StepNode currentNode; private boolean ready = false; private boolean jvmFinishedGobbling = false; private int currentGobblePosition = 0; private JVMPacman jvmPacman; private String titleString = "Getting Loaded\n\n"; private boolean urlExceptionWasThrown = false; private String cantGoFurtherString = "Unfortunately this means the applet cannot go any further.\n"; private String ioErrorMsg = "An IO Error occured while trying to read a file from the server.\n";

private String securityErrorMsg = "An security exception occured while trying to read a file from the server.\n"; private String urlErrorMsg = "This HTML file contains a malformed URL of a file required by this applet.\n"; public void init() { super.init(); ta.setEditable(false); setBackground(Color.blue); String url = getParameter("classURL"); try { this.theClassFileURL = new URL(url); } catch (MalformedURLException e) { urlExceptionWasThrown = true; ta.setText(titleString + "Bad URL: " + url + "\n\n" + urlErrorMsg + cantGoFurtherString); } url = getParameter("textURL"); try { this.theActTextURL = new URL(url); } catch (MalformedURLException e) { urlExceptionWasThrown = true; ta.setText(titleString + "Bad URL: " + url + "\n\n" + urlErrorMsg + cantGoFurtherString); } ControlPanel controlPanel = new ControlPanel(); jvmPacman = controlPanel.getJVMPacman(); setLayout(new BorderLayout(5, 5)); ta.setBackground(Color.white); add("North", new ColoredLabel("GETTING LOADED", Label.CENTER, Color.cyan)); add("South", controlPanel); add("Center", ta); } public boolean handleEvent(Event event) { return super.handleEvent(event); } public boolean action(Event evt, Object arg) { if (evt.target instanceof Button) { String bname = (String) arg; if (bname.equals("Reset")) { if (ready) { if (!currentNode.first()) { currentNode = firstNode; currentGobblePosition = 0; jvmPacman.setGobblePosition(0, currentNode.getByteCount()); ta.setText(currentNode.getString()); } } }

else if (bname.equals("Step")) { if (ready) { if (!currentNode.last()) { currentGobblePosition += currentNode.getByteCount(); currentNode = currentNode.getNext(); jvmPacman.gobbleToPosition(currentGobblePositio n, currentNode.getByteCount()); ta.setText(currentNode.getString()); } else { if (!jvmFinishedGobbling) { currentGobblePosition += currentNode.getByteCount(); jvmPacman.gobbleToPosition(currentGobblePos ition, 0); jvmFinishedGobbling = true; ta.setText("(The End)"); } } } } else if (bname.equals("Back")) { if (ready) { if (!currentNode.first()) { if (jvmFinishedGobbling) { jvmFinishedGobbling = false; currentGobblePosition -= currentNode.getByteCount(); } else { currentNode = currentNode.getPrev(); currentGobblePosition -= currentNode.getByteCount(); } jvmPacman.setGobblePosition(currentGobblePositi on, currentNode.getByteCount()); ta.setText(currentNode.getString()); } } } } return true; } public Insets insets() { return new Insets(5, 5, 5, 5); } public void start() { if (runner == null && !ready && !urlExceptionWasThrown) { runner = new Thread (this); runner.start(); } } public void stop() {

if (runner != null) { runner.stop(); runner = null; } } public void run() { InputStream conn = null; DataInputStream data = null; String line; StringBuffer buf = new StringBuffer(); ta.setText(titleString + "Loading First Of Two Files...\n"); try { conn = this.theClassFileURL.openStream(); data = new DataInputStream(new BufferedInputStream(conn)); try { while (true) { int unsignedByte = data.readUnsignedByte(); HexString hexStr = new HexString(unsignedByte, 2); buf.append(hexStr.getString()); } } catch (EOFException e) { jvmPacman.setText(buf.toString()); } try { ta.setText(titleString + "Loading Second Of Two Files...\n"); conn = this.theActTextURL.openStream(); data = new DataInputStream(new BufferedInputStream(conn)); buf.setLength(0); while ((line = data.readLine()) != null) { if (line.length() > 0 && line.charAt(0) == '*') { int starCount = line.length(); StepNode nextNode = new StepNode(buf.toString(), starCount); if (firstNode == null) { firstNode = nextNode; lastNode = nextNode; } else { lastNode.setNext(nextNode); nextNode.setPrev(lastNode); lastNode = nextNode; } buf.setLength(0); } else { buf.append(line + "\n"); } } ready = true;

currentNode = firstNode; jvmPacman.setGobblePosition(0, firstNode.getByteCount()); ta.setText(currentNode.getString()); } catch (IOException e) { ta.setText(titleString + "IO Error: " + e.getMessage() + "\n\n" + ioErrorMsg + cantGoFurtherString); } catch (SecurityException e) { ta.setText(titleString + "Security Exception: " + e.getMessage() + "\n\n" + securityErrorMsg + cantGoFurtherString); } } catch (IOException e) { ta.setText(titleString + "IO Error: " + e.getMessage() + "\n\n" + ioErrorMsg + cantGoFurtherString); } catch (SecurityException e) { ta.setText(titleString + "Security Exception: " + e.getMessage() + "\n\n" + securityErrorMsg + cantGoFurtherString); } } } class ButtonPanel extends Panel { ButtonPanel() { setLayout(new GridLayout(3, 1, 5, 5)); setBackground(Color.blue); Button b = new Button("Step"); b.setBackground(Color.lightGray); add(b); b = new Button("Back"); b.setBackground(Color.lightGray); add(b); b = new Button("Reset"); b.setBackground(Color.lightGray); add(b); } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); }

}

// The string passed to the constructor and to addText must be one line to // be printed out, excluding a closing return. class JVMPacman extends Canvas { private String theString;

private private private private

boolean stringValid = false; int currentGobblePosition; int interestingCharsCount; int charsThatFitBetweenRectanglesCount = 2;

JVMPacman() { setBackground(Color.cyan); } void setText(String passedText) { theString = passedText; stringValid = true;

}

public Dimension minimumSize() { return new Dimension(110, 60); } public Dimension preferredSize() { return new Dimension(110, 60); } public void setGobblePosition(int pos, int interesting) { // Multiply the passed position by two because the passed position represents // a byte position whereas we want currentGobblePosition to represent a // character position, and there are two hex characters for each byte. currentGobblePosition = pos * 2; interestingCharsCount = interesting * 2; repaint(); } public void gobbleToPosition(int pos, int interesting) { currentGobblePosition = pos * 2; interestingCharsCount = interesting * 2; repaint(); } public void paint(Graphics g) { Font font = getFont(); FontMetrics fm = getFontMetrics(font); int heightOfOneLine = fm.getHeight(); // Calculate x starting point Dimension dim = new Dimension(); dim = size(); int xStartingPoint = 5; // Calculate y starting point int totalHeight = heightOfOneLine * 2; int yStartingPoint = (dim.height - totalHeight) / 2; if (yStartingPoint < 5) {

}

yStartingPoint = 5;

// Calculate width of JVM rectangle. This will be heightOfOneLine more than // the stringWidth of "JVM" which I'll write in the middle of the rectangle. // This will make the border around the "JVM" the same width and height on // all sides and will equal heightOfOneLine / 2. I'll make the height of the // rectangle heightOfOneLine *2. int jvmRectangleWidth = fm.stringWidth("JVM") + heightOfOneLine; // Draw the filled rectangle g.setColor(Color.green); g.fillRoundRect(xStartingPoint, yStartingPoint, jvmRectangleWidth, totalHeight, 5, 5); // Give it a handsome black outline g.setColor(Color.black); g.drawRoundRect(xStartingPoint, yStartingPoint, jvmRectangleWidth, totalHeight, 5, 5); // Calculate width of Server rectangle. This will be heightOfOneLine more than // the stringWidth of "Server" which I'll write in the middle of the rectangle. // This will make the border around the "Server" the same width and height on // all sides and will equal heightOfOneLine / 2. I'll make the height of the // rectangle heightOfOneLine *2. int serverRectangleWidth = fm.stringWidth("Server") + heightOfOneLine; of the // Draw the filled rectangle. The x starting point is the width

// canvas minus the width of the server rectangle minus the 5 pixel margin. int xStartingPointServerRect = dim.width - serverRectangleWidth - 5; g.setColor(Color.green); g.fillRoundRect(xStartingPointServerRect, yStartingPoint, serverRectangleWidth, totalHeight, 5, 5); // Give this rectangle a handsome black outline g.setColor(Color.black); g.drawRoundRect(xStartingPointServerRect, yStartingPoint, serverRectangleWidth, totalHeight, 5, 5); int whiteRectangleWidth = xStartingPointServerRect jvmRectangleWidth - 5; if (whiteRectangleWidth > 0) {

g.setColor(Color.white); g.fillRect(jvmRectangleWidth + 5, yStartingPoint + (heightOfOneLine / 2), whiteRectangleWidth, heightOfOneLine); } // Draw "JVM" inside the rectangle g.setColor(Color.black); xStartingPoint += (heightOfOneLine / 2); int ascent = fm.getAscent(); yStartingPoint += ascent + (heightOfOneLine / 2); g.drawString("JVM", xStartingPoint, yStartingPoint); // Draw "Server" inside the rectangle int xStartingPointServerText = xStartingPointServerRect + (heightOfOneLine / 2); g.drawString("Server", xStartingPointServerText, yStartingPoint); // The string should be written so that it fits between the JVM and Server // rectangles, leaving at least 5 pixels space between the rectangle and // the string. if (stringValid && currentGobblePosition < theString.length()) { // First need to figure out how many characters will fit in // the space between the two rectangles. int xTextStartingPoint = jvmRectangleWidth + 10; int xTextEndingPoint = xStartingPointServerRect - 5; int pixelsAvailableBetweenRectangles = xTextEndingPoint xTextStartingPoint; if (pixelsAvailableBetweenRectangles < 0) { pixelsAvailableBetweenRectangles = 0; } number of amount of // Initialize the number of characters to write as the // remaining characters. This will be reduced below if this

// characters doesn't fit. int charsToWriteCount = theString.length() currentGobblePosition; between the // Check to see if the string to be displayed already fits

// two rectangles. If so, we'll just use the total number of characters // remaining as the number of characters to write. int pixelWidthOfRemainingString = fm.stringWidth(theString.substring(currentGobblePosition)); if (pixelWidthOfRemainingString > pixelsAvailableBetweenRectangles) { // The first while loop increments the charsThatFitBetweenTwoRectanglesCount

// until the width of the string in pixels just exceeds the available space. String tryThisString = theString.substring(currentGobblePosition, currentGobblePosition + charsThatFitBetweenRectanglesCount); int pixelsEaten = fm.stringWidth(tryThisString); while (pixelsEaten <= pixelsAvailableBetweenRectangles) { ++charsThatFitBetweenRectanglesCount; tryThisString = theString.substring(currentGobblePosition, currentGobblePosition + charsThatFitBetweenRectanglesCount); pixelsEaten = fm.stringWidth(tryThisString); } // The second while loop decreases the charsThatFit variable until the // width of the string in pixels is just under the available width. while (pixelsEaten > pixelsAvailableBetweenRectangles) { --charsThatFitBetweenRectanglesCount; tryThisString = theString.substring(currentGobblePosition, currentGobblePosition + charsThatFitBetweenRectanglesCount); pixelsEaten = fm.stringWidth(tryThisString); } } charsToWriteCount = charsThatFitBetweenRectanglesCount;

// Draw the interesting characters in red. g.setColor(Color.red); int redCharsCount = interestingCharsCount; if (redCharsCount > charsToWriteCount) { redCharsCount = charsToWriteCount; } String redString = theString.substring(currentGobblePosition, currentGobblePosition + redCharsCount); g.drawString(redString, xTextStartingPoint, yStartingPoint); // Draw the remaining characters in black. int blackStringStartingPosition = currentGobblePosition + redCharsCount; int blackCharsCount = charsToWriteCount - redCharsCount; if (blackStringStartingPosition < theString.length() && blackCharsCount > 0) { xTextStartingPoint += fm.stringWidth(redString); g.setColor(Color.black); g.drawString(theString.substring(blackStringStartingPos ition, blackStringStartingPosition + blackCharsCount), xTextStartingPoint, yStartingPoint);

} } }

}

class ControlPanel extends Panel { JVMPacman jvmPacman = new JVMPacman(); ControlPanel() { setLayout(new BorderLayout(5, 5)); setBackground(Color.blue); add("West", new ButtonPanel()); add("Center", jvmPacman); } public JVMPacman getJVMPacman() { return jvmPacman; } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class StepNode { private private private private private private String theString; StepNode next; StepNode prev; boolean nextValid = false; boolean prevValid = false; int byteCount = 0;

StepNode(String s, int bytes) { theString = s; byteCount = bytes; } String getString() { return theString; } int getByteCount() { return byteCount; } StepNode getNext() { // Should probably throw an exception here if !nextValid return next; } void setNext(StepNode n) { next = n; nextValid = true; }

boolean last() { return !nextValid; } StepNode getPrev() { // Should probably throw an exception here if !prevValid return prev; } void setPrev(StepNode n) { prev = n; prevValid = true; } boolean first() { return !prevValid; } } // I used this class because I can't seem to set the background color of // a label. I only want a label, but I want the backgound to be gray. class ColoredLabel extends Panel { private Label theLabel; ColoredLabel(String label, int alignment, Color color) { setLayout(new GridLayout(1,1)); setBackground(color); theLabel = new Label(label, alignment); } add(theLabel);

public void setLabelText(String s) { theLabel.setText(s); } public Insets insets() { return new Insets(0, 0, 0, 0); }

}

class HexString { private final String hexChar = "0123456789ABCDEF"; private StringBuffer buf = new StringBuffer(); void Convert(int val, int maxNibblesToConvert) { buf.setLength(0); int v = val;

for (int i = 0; i < maxNibblesToConvert; ++i) { if (v == 0) { if (i == 0) { buf.insert(0, '0'); } break; } // Get lowest nibble int remainder = v & 0xf; // Convert nibble to a character and insert it into the beginning of the string buf.insert(0, hexChar.charAt(remainder)); // Shift the int to the right four bits v >>>= 4;

} }

HexString(int val, int minWidth) { Convert(val, minWidth); int charsNeeded = minWidth - buf.length(); for (int i = 0; i < charsNeeded; ++i) { buf.insert(0, '0'); } } public String getString() { } } ASCII FILE for previous source code Step 1. Magic Number hex bytes --------CAFEBABE name ---magic return buf.toString();

First the JVM must make sure that the class file starts with the proper magic number. In this case our JVM will be happy because it will find the CafeBabe magic right where it's supposed to be. By the way, all numbers are stored in the class file in big-endian order, which means the higher order bytes come first. The very first byte of every class file, therefore, will be 0xCA. **** Step 2. Version Numbers hex dec name

--0003 002D

--3 45

---minor_version major_version

Next, the JVM must make sure that it recognizes and fully understands the format of the class file being loaded. If either the major or minor version number is higher than those version numbers for which this JVM was implemented, the JVM must reject the class file. In this case, our JVM is relieved to find that the file has major version 45 and minor version 3, of which it has intimate knowledge. **** Step 3. Constant Pool Count 0011 17 constant_pool_count

The next two bytes make up an unsigned short integer which indicates the number of elements in the constant pool array. In this case the constant pool will have 17 elements, but because the zeroeth element doesn't appear in the class file, the JVM will expect to find elements 1 through 16 next in the stream. ** Step 4. constant_pool[1] 07 000C 7 12 tag name_index

Each constant pool element contains a structure whose type is indicated by the first byte of the element which is interpreted as a type tag. The first constant pool entry is a CONSTANT_Class_info structure. The JVM knows this because it finds a 7, which means CONSTANT_Class, in the tag byte position. Aside from the tag the CONSTANT_Class_info structure has only a name_index, which in this case is 12. This is an index into the constant pool. The JVM doesn't know it yet, because it hasn't read in the 12th element of the constant pool, but the 12th element will be the string "java/lang/Object", which is the name of the superclass of this class. Because class Act doesn't explicitly descend from any other class, it by default descends from class Object. Note that this is the first constant pool element and that it already has index 1. constant_pool[0] doesn't appear in the class file. *** Step 5. constant_pool[2] 07 7 tag

000D

13

name_index

The 2nd constant pool entry is another CONSTANT_Class_info structure. The JVM knows this because it again finds a 7, which still means CONSTANT_Class, in the tag byte position. The name_index here, however, is 13. constant_pool[13] will be the string "Act", although once again the JVM doesn't know this yet because it hasn't read that far into the constant pool. At this point the JVM only knows that when it does get around to the 13th constant pool element, that element will contain the name of the class which is represented by the CONSTANT_Class_info structure occupying constant_pool[2]. constant_pool[2] therefore represents the "this" class, class Act, which this class file defines. *** Step 6. constant_pool[3] 0A 0001 0004 10 1 4 tag class_index name_and_type_index

constant_pool[3] contains a CONSTANT_Methodref_info structure, indicated by the tag of 10, which means CONSTANT_Methodref. A CONSTANT_Methodref_info structure represents a method and records its name, type, and the class to which it belongs. The first two bytes after the tag, the class_index, form an index into the constant pool, in this case a 1. constant_pool[1] represents the superclass Object, so this method should be declared in Object. The next two bytes make up the name_and_type_index, which is an index into the constant pool pointing to a CONSTANT_NameAndType structure. In this case name_and_type_index is 4 and constant_pool[4] defines "void <init>()". Because class Act doesn't declare its own constructor, the javac compiler wrote one of its own which calls <init>() in superclass Object. ***** Step 7. constant_pool[4] 0C 000E 0010 12 14 16 tag name_index descriptor_index

constant_pool[4] is a CONSTANT_NameAndType structure identified by its initial

tag of 12, which stands for CONSTANT_NameAndType. Following the tag byte is the name_index, which is 14. constant_pool[14] will be the string "<init>". Next is the descriptor_index, which is 16. constant_pool[16] will be the string "()V". "<init>" is the name of the method being described. "()V" is the type. In plain Java it would look like "void <init>()". "()V" is a method descriptor. The "()" indicates that there are no arguments to "<init>". The "V" indicates the return type of "<init>" is void. ***** Step 8. constant_pool[5] 01 000D 436F6E7374616E7456616C7565 bytes[length] 1 13 tag length

"ConstantValue"

constant_pool[5] is a CONSTANT_Utf8_info structure as identified by its tag of 1, which means CONSTANT_Utf8. CONSTANT_Utf8 is how constant strings are encoded in the constant pool. The UTF-8 format allows any 16 bit character to be represented by either 1, 2, or 3 bytes. The characters '\u0001' to '\u007F' occupy only one byte. Other characters require 2 or 3 bytes, however, these characters are expected to appear less frequently than the characters that require only 1 byte. By this means the full 16 bit Unicode character set can be supported by the class file without using as much space as would likely be used by just making each character 16 bits long. The tag byte of a CONSTANT_Utf8_info structure is followed by a length, in this case 13, which indicates the length in bytes of the UTF-8 format string that follows. In this case the string spells out "ConstantValue". This string is used internally by the class file in a manner that will be described later in the file loading simulation. There is no trailing null character in a CONSTANT_Utf8_info.bytes as that would unnecessarily waste bandwidth. **************** Step 9. constant_pool[6] 01 000D 1 13 tag length

646F4D617468466F7265766572 bytes[length]

"doMathForever"

constant_pool[6] is a CONSTANT_Utf8_info structure which contains the string "doMathForever". This is the name of the lone method defined in class Act. **************** Step 10. constant_pool[7] 01 000A 457863657074696F6E73 1 10 "Exceptions" tag length bytes[length]

constant_pool[7] is a CONSTANT_Utf8_info structure which contains the string "Exceptions". ************* Step 11. constant_pool[8] 01 1 000F 15 4C696E654E756D6265725461626C65 "LineNumberTable" bytes[length] tag length

constant_pool[8] is a CONSTANT_Utf8_info structure which contains the string "LineNumberTable". ****************** Step 12. constant_pool[9] 01 000A 536F7572636546696C65 1 10 "SourceFile" tag length bytes[length]

constant_pool[9] is a CONSTANT_Utf8_info structure which contains the string "SourceFile". ************* Step 13. constant_pool[10] 01 000E 4C6F63616C5661726961626C6573 1 14 "LocalVariable" tag length bytes[length]

constant_pool[10] is a CONSTANT_Utf8_info structure which contains the string "LocalVariable". ***************** Step 14. constant_pool[11] 01 0004 436F6465 1 4 tag length

"Code"

bytes[length]

constant_pool[11] is a CONSTANT_Utf8_info structure which contains the string

"Code". ******* Step 15. constant_pool[12] 01 0010 6A6176612F6C616E672F4F626A656374 bytes[length] 1 16 tag length "java/lang/Object"

constant_pool[12] is a CONSTANT_Utf8_info structure which contains the string "java/lang/Object". This is the fully qualified class name of "java.lang.Object" with the dots changed to slashes. (The slashes are there in place of the dots because of historical reasons.) ******************* Step 16. constant_pool[13] 01 0003 416374 1 3 "Act" tag length bytes[length]

constant_pool[13] is a CONSTANT_Utf8_info structure which contains the string "Act". This is the name of the class which is being defined by this file. ****** Step 17. constant_pool[14] 01 0006 3C696E69743E 1 6 "<init>" tag length bytes[length]

constant_pool[14] is a CONSTANT_Utf8_info structure which contains the string "<init>", the name of a method in the superclass, Object. ********* Step 18. constant_pool[15] 01 1 000B 11 736E697065742E6A617661 "snipet.java" tag length bytes[length]

constant_pool[15] is a CONSTANT_Utf8_info structure which contains the string "snipet.java", the name of the source file in which class Act was defined. ************** Step 19. constant_pool[16] 01 0003 282956 1 3 "()V" tag length bytes[length]

constant_pool[15] is a CONSTANT_Utf8_info structure which contains the string

"()V", a method descriptor which translates to a method that takes no arguments and returns void. ****** Step 20. Access Flags 0000 access_flags

The access flags are a two byte unsigned integer that is composed by bitwise oring bitmasks for individual flags that represent modifiers of the class or interface defined by this file. For example, ACC_PUBLIC is 0x0001 and ACC_FINAL is 0x0010. A class declared to be both public and final would have its access flags set to 0x0011, or (ACC_PUBLIC | ACC_FINAL). In this case no access_flags are set because the class being defined, class Act, was not declared to be public, final, or abstract. Classes which are declared with these modifiers would have the appropriate bitmasks from ACC_PUBLIC, ACC_FINAL, and ACC_ABSTRACT ored together to make the resultant access_flags. Also, if a class file defines an interface and not class, then an ACC_INTERFACE bit is set in access_flags. This is how the JVM knows whether a class or an interface is being defined by the file. ** Step 21. This Class and Super Class 0002 0001 2 1 this_class super_class

this_class is a two byte unsigned integer index into the constant pool, where constant_pool[this_class] is a CONSTANT_Class_info structure representing the class defined by this file. In our case it is the CONSTANT_Class_info structure for class Act. super_class is a two byte unsigned integer index into the constant pool, where constant_pool[super_class] is a CONSTANT_Class_info structure representing the superclass from which the class defined by this file decends. In our case it is the CONSTANT_Class_info structure for class Object. **** Step 22. Interfaces Count and Fields Count 0000 0000 0 0 interfaces_count fields_count

interfaces_count is a two byte unsigned integer which indicates the number of interfaces implemented by this class. Because class Act implements no interfaces, interfaces_count is zero in this case. fields_count is a two byte unsigned integer which indicates the number of fields (class or instance variables) implemented by this class. Because class Act has no class or instance variables, fields_count is zero in our case. **** Step 23. Methods Count 0002 2 methods_count

methods_count is a two byte unsigned integer indicating the number of methods defined by this class. The count does not include any methods inherited from superclasses, only those methods explicitly defined in this class. In this case methods_count is 2 because doMathForever() is defined in the source file and the constructor Act() is defined by the compiler. The JVM will expect an array of 2 method_info structures to immediately follow this methods_count. ** Step 24. doMathForever()'s Access Flags, Name Index, and Descriptor Index 0009 0006 0010 6 16 access_flags name_index descriptor_index

This is the beginning of the array of method_info structures that immediately follows the methods_count. The first method_info structure, methods[0], gives information about the doMathForever() method. The second method_info structure, methods[1], gives information about the Act() constructor. The first three parts of methods[0] are shown here. access_flags gives the modifiers with which the method was declared. In this case access_flags is a 0x0009 which equates to (ACC_PUBLIC | ACC_STATIC). If you look back at the source code, the doMathForever() method is indeed declared public and static. The name_index indicates the constant pool entry where the name of the method is stored. In this case name_index is 6 and constant_pool[6] is indeed the UTF-8

string "doMathForever". The descriptor_index indicates the constant pool entry where the descriptor of this method is stored. In this case descriptor_index is 16 and constant_pool[16] is the string "()V". This descriptor string indicates that doMathForever() is a method which takes no arguments and returns void. ****** Step 25. doMathForever() Attributes 0001 1 attributes_count

methods[0].attributes[0] 000B 11 attribute_name_index 00000030 48 length Following the descriptor_index of a method is its attributes_count. In this case there is only 1 attribute for the doMathForever() method. The list of attributes follows the attributes_count. Here there is only one attribute, attributes[0]. The first two bytes of the attribute are the attribute_name_index. The attribute_name_index is the index of the constant pool entry which contains the name of the attribute. In this case attribute_name_index is 11 and constant_pool[11] is "Code". Therefore this is the Code attribute of doMathForever() which will contain, among a few other items, the actual bytecode sequence for the method. The length word indicates the number of bytes in the attribute, in this case 48 bytes. This means 48 bytes will follow this length word. ******** Step 26. doMathForever() Max Stack and Max Locals 0002 0001 2 1 max_stack max_locals

Max stack is a two byte unsigned integer that indicates the maximum number of entries on the JVM's operand stack at any point in the method. Max locals is a two byte unsigned integer that indicates the number of local variables slots used by the method. Each local variable slot is a four byte word. In this case, max_stack is 2. If you look back at the Eternal Math applet from last month's article and watch the operand stack as the JVM executes the doMathForever() bytecodes, you will see that the operand stack never has more than two words in it.

The max_locals is 1 in this case, because doMathForever() takes no arguments and has only one variable, i. If you look back at the Eternal Math applet you can see that there is only one word in the local variables section of doMathForever()'s stack frame, the integer i. **** Step 27. doMathForever() Bytecodes and Exception Table 0000000C 033B8400011A05683BA7FFF9 0000 12 0 code_length code[code_length] exception_table_length sequence for the to the bytecodes

code_length indicates the number of bytes in the bytecode this method. The actual bytecodes follow the code_length. In this case doMathForever() method bytecode sequence fills 12 bytes. If you look back Eternal Math applet from last month's article you will see that these are the being executed in that simulation. pc -0 1 2 5 6 7 8 9 instruction ----------03 3B 840001 1A 05 68 3B A7FFF9 mnemonic -------iconst_0 istore_0 iinc 0 1 iload_0 iconst_2 imul istore_0 goto 2

exception_table_length indicates the number of exceptions that are caught in the method. In the case of doMathForever(), exception_table_length is zero because no exceptions are explicitly caught in this method. For methods that do catch exceptions, an array of exception handler structures immediately follows the exception_table_length. One other thing you may notice at this point is that these bytecodes haven't been assigned a place in memory yet. This is done by the JVM when it loads the class file. Therefore, the value I called pc is really the offset from the actual program counter address at which this bytecode sequence is loaded by a JVM. ****************** Step 28. Attributes of the doMathForever() Code Attribute

0001 0008 00000012 0004

1 8 4

attributes_count attribute_name_index 18 attribute_length line_number_table_length

The doMathForever() "Code" attribute itself has a "sub-attribute" section. I.e., the attributes listed here belong to the "Code" attribute of doMathForever(). attributes_count is 1, which indicates there is only one attribute belonging to the "Code" attribute of doMathForever(). attribute_name_index is an 8, and constant_pool[8] is the string "LineNumberTable". This is the name of the attribute. attribute_length gives the length of attribute "LineNumberTable" as 18. The line_number_table_length is 4, meaning that there will be 4 line_number_table entries immediately following. If you look back at the snipet of Java code which defines doMathForever() you'll see that there are only 4 lines of code which get translated to bytecodes: line line line line 4: i = 0; 5: while (true) { 6: i += 1; 7: i *= 2;

These four lines will be matched up with the corresponding bytecodes in the line_number_table that follows. ********** Step 29. Line Number Table for doMathForever() hex dec ----line_number_table[0] 0000 0 0004 4 line_number_table[1] 0002 2 0006 6 line_number_table[2] 0005 5 istore_0 0007 7 line_number_table[3] 0009 9 0005 5 name ---start_pc line_number start_pc line_number start_pc line_number start_pc line_number i *= 2 goto 2 while (true) { what it refers to ------------------iconst_0, istore_0 int i = 0; iinc 0 1

i += 1

iload_0, iconst_2, imul,

The above table associates each line in the doMathForever() code with its corresponding bytecode instruction. The start_pc value of each table entry indicates the zero based byte position inside the doMathForever() bytecode sequence. The line_number value of each table entry is the line number of the source file that corresponds to the start_pc position in the bytecode sequence. The line number information is of general utility. It is useful to debuggers, for example. It also allows the line number at which an exception is thrown to be written to the Java console. This is the last of the information in the class file about the doMathForever() method. Next will come the information about the other method of this class, the constructor Act(). **************** Step 30. Act()'s Access Flags, Name Index, and Descriptor Index methods[1] 0000 000E 0010 access_flags name_index descriptor_index

14 16

This is the beginning of the second method_info structure, methods[1], which gives information about the Act() constructor. The first three parts of methods[1] are shown here. access_flags gives the modifiers with which the method was declared. In this case access_flags is a 0x0000 which means it's neither public, private, protected, static, final, synchronized, native, or abstract. It's just a plain old method. The name_index indicates the constant pool entry where the name of the method is stored. In this case name_index is 14 and constant_pool[14] is indeed the UTF-8 string "<init>". "<init>" is a special internal method name used to represent constructors. The descriptor_index indicates the constant pool entry where the descriptor of this method is stored. In this case descriptor_index is 16 and constant_pool[16] is the string "()V". This descriptor string indicates that <init>() or Act() is a

method which takes no arguments and returns void. ****** Step 31. Act() Attributes 0001 000B 0000001D 1 11 attributes_count attribute_name_index 29 attribute_length

Following the descriptor_index of a method is its attributes_count. In this case there is only 1 attribute for the Act() method. The list of attributes follows the attributes_count. Here there is only one attribute, attributes[0]. The first two bytes of the attribute are the attribute_name_index. The attribute_name_index is the index of the constant pool entry which contains the name of the attribute. In this case attribute_name_index is 11 and constant_pool[11] is "Code". Therefore this is the Code attribute of doMathForever() which will contain, among a few other items, the actual bytecode sequence for the method. The length word indicates the number of bytes in the attribute, in this case 29 bytes. This means 29 bytes will follow this length word. ******** Step 32. Act() Max Stack and Max Locals 0001 0001 1 1 max_stack max_locals

Max stack is a two byte unsigned integer that indicates the maximum number of entries on the JVM's operand stack at any point in the method, in this case 1. Max locals is a two byte unsigned integer that indicates the number of local variables slots used by the method, in this case 1. Each local variable slot is a four byte word. **** Step 33. Act() Bytecodes and Exception Table 00000005 2AB70003B1 0000 5 code_length code[code_length] exception_table_length

0

code_length indicates the number of bytes in the bytecode sequence for this method. The actual bytecodes follow the code_length. In this case the Act() method bytecode sequence fills 5 bytes. This method was generated by the compiler and doesn't appear in the Java source file for class Act. pc instruction mnemonic

------------------0 2A aload_0 1 B70003 invokenonvirtual #3 <Method java.lang.Object.<init>()V> 4 B1 return If you look at the operand for the second instruction, invokenonvirtual (bytecode B7), you will see a 0003. If you go look at constant_pool[3] you'll find a name and type constant that describes the <init> method of java.lang.Object. exception_table_length indicates the number of exceptions that are caught in the method. In the case of Act(), exception_table_length is zero because no exceptions are explicitly caught in this method. For methods that do catch exceptions, an array of exception handler structures immediately follows the exception_table_length. *********** Step 34. Line Number Table Attribute of the Act() Code Attribute hex --0001 0008 00000006 0001 dec --1 8 1 name ---attributes_count attribute_name_index 6 attribute_length line_number_table_length

The Act() "Code" attribute itself has a "sub-attribute" section. I.e., the attributes listed here belong to the "Code" attribute of Act(). attributes_count is 1, which indicates there is only one attribute belonging to the "Code" attribute of Act(). attribute_name_index is an 8, and constant_pool[8] is the string "LineNumberTable". This is the name of the attribute. attribute_length gives the length of attribute "LineNumberTable" as 6. The line_number_table_length is 1, meaning that there will be only 1 line_number_table entry immediately following. ********** Step 35. Line Number Table for Act() hex --0000 #3, return 0002 dec --0 2 name ---start_pc line_number what it refers to ----------------aload_0, invokenonvirtual class Act {

The above table associates a line in the Java source file with the starting instruction of the Act() method. The start_pc value of the table entry indicates the zero based byte position inside the Act() bytecode sequence. The line_number value of the table entry is the line number of

the source file that corresponds to the start_pc position in the bytecode sequence. This is the last of the information in the class file about the Act() method, which was the last method to be described by this file. The last section of the class file follows, the general class file attributes. **** Step 36. General Attributes 0001 0009 00000002 000F 1 9 15 attributes_count attribute_name_index 2 attribute_length sourcefile_index

A class file can have any number of attributes at the end. In this case attributes_count is a 1, so there is only 1 attribute here. The attribute_name_index is 9, and constant_pool[9] is the string "SourceFile". Therefore, this is the "SourceFile" attribute. attribute_length gives the length of the attribute as 2, which means that two bytes will follow the attribute_length field. The last two bytes are the sourcefile_index, which in this case is 15. constant_pool[15] is the string "snipet.java", which as it happens, is the name of the source file I compiled with javac to generate this file, Act.class. **********

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->