The next four bytes describe the number of fields and methods in the class: 000163 0000 0 fields 000165 0002 2 methods This class has no fields and two methods. Fields and methods have identical formats. This class has no fields, so the next bytes form the definition of the first method. Method 0: 000167 0009 access flags = 9 000169 001a name = #26<main> 00016b 001b descriptor = #27<([Ljava/lang/String;)V> A field or method starts off with two bytes of access flags. The meaning of these bits, from right to left, is shown in Figure 9.2 and Table 9.4. Figure 9.2. Method access flags with value 9The main method is both public and static, so bits 1 and 4 are set. This yields the value 9, which is the value of the access flags field. Following the access flags are two two-byte constant pool references, giving the name of the field or method (main) and the type descriptor (([Ljava/lang/String;)V). The type descriptor says that this is a method that takes an array of Strings and returns void.
After the general method or field information, the file contains a list of attributes. Field and method definitions have identical formats, giving the name, type, and access information, followed by a list of attributes. Fields and methods have different kinds of attributes. Methods usually have a single attribute giving the implementation of the method. Most fields don't have any attributes at all. Only the ConstantValue attribute is defined for fields, which specifies the initial value of some fields. ConstantValue is discussed in section 9.6.1. The attribute for this method begins 00016d 0001 1 method attributes: method attribute 0 00016f 0015 name = #21<Code> 000171 00000025 length = 37 Every attribute begins with two pieces of information: a constant pool entry indicating the name of the attribute as a UTF8 constant and four bytes indicating the length of the attribute data. The data follow. In this case, the name of the attribute is Code, and the Code attribute takes up the next 37 bytes of the file. The name of the attribute may be anything you can fit into a UTF8 constant. The Java Virtual Machine Specification defines the name and data structure of some attributes, such as the Code attribute shown here. A JVM implementation ignores any attributes it does not recognize. The next 37 bytes form the value of the Code attribute. They begin with some information about the resources the method requires to run: 000175 0002 max stack: 2 000177 0001 max locals: 1 This information indicates the maximum height of the stack and the maximum number of local variables permitted in the method. These let the program know how much space to allocate when the method is run. Since these are 16-bit numbers, the stack is limited to 65,536 slots and 65,536 local variables. Fortunately, real programs never require this much space. This information is checked during the verification process, and if the program attempts to use more stack space or more local variables than requested here, then the class will be rejected before it gets a chance to run. For more information on how this is determined, see chapter 6. After the method information, the class file includes the actual code for the method. 000179 00000009 code length: 9 00017d b20006 0000 getstatic #6 000180 1201 0003 ldc #1 000182 b60007 0005 invokevirtual #7 000185 b1 0008 return The first four bytes indicate that the code is nine bytes long. Since this is a four-byte number, the code may be up to four gigabytes long. However, other limitations limit practical code size to 64 kilobytes. The next nine bytes are interpreted as Java virtual machine instructions, or bytecodes. The rightmost column of the DumpClass output gives the address of the code relative to the beginning of the code, followed by the instruction at that address. An instruction consists of a single-byte opcode and a few bytes of arguments. The opcode determines how many bytes are used as arguments for the instruction. The next instruction begins immediately after the arguments for this instruction. Some instructions have no arguments, like the return instruction at offset 185. Others, like the getstatic instruction at location 17d, use two bytes to represent a constant in the constant pool. The getstatic instruction at 17d points to constant 6, a Fieldref constant. The ldc instruction at location 180 is a little unusual. Its argument is only one byte wide, but it's still interpreted as a constant reference. This constant must be an Integer, Float, or String constant. There are two forms of the ldc instruction: ldc and ldc_w. The former requires one argument byte, which is interpreted as a reference to a constant between 0 and 255. The latter uses two bytes for its argument and may refer to any constant. In either case the constant pool entry must be a Integer, Float, Double, Long, or String entry. The ldc instruction is provided because it requires less space in the class file. The constant entries are usually placed in the lowest values of the constant pool so that the ldc instruction can be used instead of ldc_w. Rewriting all the constants in Oolong form, the code for the method is getstatic java/lang/System/out Ljava/io/PrintStream; ldc "Hello, world" invokevirtual java/io/PrintStream/println (Ljava/lang/String;)V return This is the same code we've seen elsewhere in the book to write the Hello, world program in Oolong. The first instruction fetches the value of out, which is a PrintStream used for writing output to the console. The second instruction loads the constant "Hello, world", which is to be printed. The third instruction invokes the println method on out, passing it "Hello, world" as an argument. The final instruction terminates the method. 000186 0000 0 exception table entries: Following the bytecodes is an exception table, which begins with a two-byte count of the number of exception table entries. These entries determine how exceptions are handled when they are raised during the execution of this method. This method doesn't catch any exceptions, so the length of the table is 0. Following the exception handler table, the code attribute may have attributes of its own. The attributes follow the same format as before: name, length, and a collection of bytes. Code attributes are used to store information about the code itself, such as debugging information. The main method has one attribute, a LineNumberTable: 000188 0001 1 code attributes: code attribute 0: 00018a 0011 name = #17<LineNumberTable> 00018c 0000000a length = 10 Line number table: 000190 0002 length = 2 000192 0000 start pc: 0 000194 0005 line number: 5 000196 0008 start pc: 8 000198 0003 line number: 3 This line number table has two entries in it, as indicated by the 0x0002 at byte 0x0190. Each entry maps a location in the bytecodes (called the start pc) to a line in the file (called the line number). The start pc and the line number are each given as a two-byte number, which tends to limit Java source files to 65,536 lines and the bytecode to 65,536 bytes. The first entry maps the beginning of the method (at location 0) to line 5 of the source, which is the line containing the println. The second entry is for the return instruction at location 8. Since there is no explicit return in the source code, the compiler maps the return instruction to the first line of the method, on source line 3. Instructions that don't have explicit mappings are assumed to be on the same line as the previous instruction in the file. Since the instructions at 3 and 5 aren't explicitly mapped, they're all assumed to be part of the source line 5, since that's where 0 is mapped to. This is reasonable, since they're all part of the println statement on line 5. That's all there is to the main method of this program. However, the class file indicated that there was another method. Besides, there are still some bytes left in the file. Any file that ends prematurely or that has junk bytes at the end is rejected by the verification algorithm. Method 1: 00019a 0000 access flags = 0 00019c 001c name = #28<<init>> 00019e 001f descriptor = #31<()V> The method is named <init>; it's the constructor for the hello class. Since no constructor was given in the Java source, the Java compiler added this method automatically. This is what Dumpclass prints for <init>: 0001a0 0001 1 method attributes: method attribute 0 0001a2 0015 name = #21<Code> 0001a4 0000001d length = 29 0001a8 0001 max stack: 1 0001aa 0001 max locals: 1 0001ac 00000005 code length: 5 0001b0 2a 00000000 aload_0 0001b1 b70008 00000001 invokespecial #8 0001b4 b1 00000004 return 0001b5 0000 0 exception table entries: 0001b7 0001 1 code attributes: code attribute 0: 0001b9 0011 name = #17<LineNumberTable> 0001bb 00000006 length = 6 Line number table: 0001bf 0001 length = 1 0001c1 0000 start pc: 0 0001c3 0001 line number: 1 Like the main method, the <init> method has exactly one attribute: its Code attribute. It promises to push at most one thing at a time onto the stack and uses only one local variable. The code for this method is equivalent to the Oolong: aload_0 invokespecial java/lang/Object/<init> ()V return This code invokes the superclass constructor on the object, which is the argument in local variable 0. If this method were not provided, it would not be possible to create an instance of the hello object, since an object must be constructed before it can be used. The verification algorithm requires that each constructor call a superclass constructor to ensure that objects are always completely initialized. Like the main method, <init> also has an attribute that contains a table of line numbers. It's a little odd that there can be a line number table for a method that doesn't have any source code, but the compiler adds it anyway for the benefit of debuggers. All the instructions are mapped to the beginning of the class on line 1. |