JVM JIT and running mode

JIT

At the beginning of Java, all code are executed in interpreted manner, I.E execute code line by line after interpretation which would result in slowness. Especially for code that are frequently executed.

So later JIT was introduced so that when when some code are frequently executed, it becomes ‘Hot Spot Code’ and would be compiled to  machine code. Typically there 2 types of hot-spot-code:

  1. A function that are called very frequently
  2. The code in loop.

JVM maintains a count as of how many time a function is executed. If this count exceeds a predefined limit JIT compiles the code into machine language which can directly be executed by the processor (unlike the normal case in which javac compile the code into bytecode and then java – the interpreter interprets this bytecode line by line converts it into machine code and executes).

Also next time this function is calculated same compiled code is executed again unlike normal interpretation in which the code is interpreted again line by line.

Server vs client mode

when we check java version, there are 3 lines, we will check what the the 3rd line.


---> java -version

java version "1.8.0_66"

Java(TM) SE Runtime Environment (build 1.8.0_66-b17)

Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

Hotspot jvm has 2 mode: client and server. we can use java -server xxx / java -client xxx to start. The former is more heavier on compilation and optimization hence with longer startup time.

int/comp/mixed

use java -Xint/comp/mixed to trigger, mixed is the default. 

java -Xint -version 

will show that it is in the interpreted mode so that code will be interpreted and executed line by line which would be quite slow specially in loop. On the other hand, -Xcomp will execute the code after compile all the code to machine code. 

中文blog-关于JVM的类型和模式

How Java Debug works

You can just attach your IDE to a running application (which has been runned for debug as we’ll see later), or you can even debug it from command line. And the application you debug can even be be in a different machine.

The magic lies in where the debug information actually resides. Apparently people normally think that is the IDE that knows how to debug your programs, but the truth is that is the program who knows how to debug itself, and makes that information available to whoever wants to use it.

The way it works is basically the following. When you compile a program, the .class files get debug information within them, like line numbers or local variables that are made accessible to others who want to access this information. You can then run the program in debug mode passing the following options to your java program execution(you can of course run any java program like this, including mvn goals, appllication servers, etc)

User Jdb

There are many ways to start a jdb session. The most frequently used way is to have jdb launch a new Java Virtual Machine (VM) with the main class of the application to be debugged. This is done by substituting the command jdb for java in the command line. For example, if your application’s main class is MyClass, you use the following command to debug it under JDB:

C:\> jdb MyClass 

When started this way, jdb invokes a second Java VM with any specified parameters, loads the specified class, and stops the VM before executing that class’s first instruction.

Another way(used by ide)

java -agentlib:jdwp=transport=dt_shmem,address=jdbconn,server=y,suspend=n Sum 3 4

You can then attach jdb to the VM with the following commmand:

C:\> jdb -attach jdbconn

This line basically says: Run this program in debug mode, use the jdwp protocol, with a socket that listens to port 4000 and waits for connections to continue.

The jdwp protocol is a communication protocol used by the Java application to receive and issue commands and reply to them.

For example, you can connect to this port when the application is running an issue commands like “print variablex” to know the value of a variable, “stop at x” to set a breakpoint, etc. The application issues notification commands like “breakpoint reached”.
The truth is that the protocol is a little more complex than this, but this is enough to know to illustriate the point.

With the previous said, we can see that it would be even possible to debug an application with the use of Telnet! (we’ll see later)

Well, enough theory. Let’s see an example Any simple example will do. We’ll make a simple program that takes two parameters from command line and prints the sum. The program won’t be well designed (In the sense that will include some useless temp variables, no validations, etc) but will do to illustrate the example.

class Sum{
    public static void main(String[] args){
        int sum1 = Integer.parseInt(args[0]);
        int sum2 = Integer.parseInt(args[1]);
        int suma= sum1+sum2;
        System.out.println("La suma es "+suma);
    }
}

So we compile it: javac -g Sum.java (the g option adds extra debug info to the class. Like local variable names)
And we run it in debug mode:

C:\> jdb Sum

you’ll get some output like
Initializing jdb …
>

That’s it, you have a debug session started. Now the interesting. Execute the following in your jdb session:

stop at Sum:6

You now have a breakpoint on line 6. execute run on the session, and the program will run until that breakpoint. you’ll get the output

Deferring breakpoint Sum:6.
It will be set after the class is loaded.

We can then type ‘run‘ to start jvm.

Now let’s see the value of our variables: run the following commands (one at a time) on the jdb session and see the results.

print sum1
print sum2
print suma
locals
set suma = 10
locals
cont

This is pretty cool stuff. You can debug your program from command line.

 

From here and here

Class Loaders and the Parent-Delegation Model a

How the Java Launcher Finds Classes

The Java launcher, java, initiates the Java virtual machine. The virtual machine searches for and loads classes in this order:

  • Bootstrap classes – Classes that comprise the Java platform, including the classes in rt.jar and several other important jar files. Bootstrap classes are in the rt.jar and several other jar files in the jre/lib directory.
  • Extension classes – Classes that use the Java Extension mechanism. These are bundled as .jar files located in the extensions directory.Extension classes are classes which extend the Java platform: .jar file in the extension directory, jre/lib/ext
  • User classes – Classes defined by developers and third parties that do not take advantage of the extension mechanism. You identify the location of these classes using the -classpath option on the command line (the preferred method) or by using the CLASSPATH environment variable.To find user classes, the launcher refers to the user class path — a list of directories, JAR archives, and ZIP archives which contain class files.

More about how to find these classes 

Now let’s get our hands dirty with some real code. Consider the following example: class A instantiates class B.

	public class A {
	  public void doSomething() {
  	 B b = new B();
   	 b.doSomethingElse();
	  }
	}

The statement B b = new B() is semantically equivalent to B b = A.class.getClassLoader().loadClass(“B”).newInstance()

To better visualize the parent-delegation model, imagine a Java application creates a user-defined class loader named “Grandma.” Because the application passesnull to Grandma’s constructor, Grandma’s parent is set to the bootstrap class loader. Time passes. Sometime later, the application creates another class loader named “Mom.” Because the application passes to Mom’s constructor a reference to Grandma, Mom’s parent is set to the user-defined class loader referred to affectionately as Grandma. More time passes. At some later time, the application creates a class loader named, “Cindy.” Because the application passes to Cindy’s constructor a reference to Mom, Cindy’s parent is set to the user- defined class loader referred to as Mom.

Now imagine the application asks Cindy to load a type named java.io.FileReader. When a class that follows the parent delegation model loads a type, it first delegates to its parent — it asks its parent to try and load the type. Its parent, in turn, asks its parent, which first asks its parent, and so on. The delegation continues all the way up to the end-point of the parent-delegation chain, which is usually the bootstrap class loader. Thus, the first thing Cindy does is ask Mom to load the type. The first thing Mom does is ask Grandma to load the type. And the first thing Grandma does is ask the bootstrap class loader to load the type. In this case, the bootstrap class loader is able to load (or already has loaded) the type, and returns the Class instance representing java.io.FileReader to Grandma. Grandma passes this Class reference back to Mom, who passes it back to Cindy, who returns it to the application.

Note that given delegation between class loaders, the class loader that initiates loading is not necessarily the class loader that actually defines the type. In the previous example, the application initially asked Cindy to load the type, but ultimately, the bootstrap class loader defined the type. In Java terminology, a class loader that is asked to load a type, but returns a type loaded by some other class loader, is called an initiating class loader of that type. The class loader that actually defines the type is called the defining class loader for the type. In the previous example, therefore, the defining class loader for java.io.FileReader is the bootstrap class loader. Class Cindy is an initiating class loader, but so are Mom, Grandma, and even the bootstrap class loader. Any class loader that is asked to load a type and is able to return a reference to the Class instance representing the type is an initiating loader of that type.

For another example, imagine the application asks Cindy to load a type named com.artima.knitting.QuiltPattern. Cindy delegates to Mom, who delegates to Grandma, who delegates to the bootstrap class loader. In this case, however, the bootstrap class loader is unable to load the type. So control returns back to Grandma, who attempts to load the type in her custom way. Because Grandma is responsible for loading standard extensions, and the com.artima.knitting package is wisely installed in a JAR file in the standard extensions directory, Grandma is able to load the type. Grandma defines the type and returns the Class instance representing com.artima.knitting.QuiltPattern to Mom. Mom passes this Class reference back to Cindy, who returns it to the application. In this example, Grandma is the defining loader of the com.artima.knitting.QuiltPattern type. Cindy, Mom, and Grandma — but not the bootstrap class loader — are initiating class loaders for the type.

Note: We can always create our own classLoader and override the loadClass implementation to bypass the default system class loader though it is not recommended for security reasons(meaning we should delegate to bootstrap-classloader first). If we are in container, it is not easy since it always searches the bootstrap class loader first, then the local app lib, then the container lib. More detail in my other post.

ClassCastException

Let’s demonstrate a case for ClassCastException. We’ll modify the initial example to use a factory in order to provide the implementation of a class that provides the greeting message. Sounds contrived but this is quite a common pattern.

public class HelloServlet extends HttpServlet {
   protected void doGet(HttpServletRequest request, 
                                    HttpServletResponse response) 
                                    throws ServletException, IOException {
       PrintWriter out = response.getWriter();
    out.print(((Util)Factory.getUtil()).sayHello());
}
 
class Factory {
     public static Object getUtil() {
          return new Util();
     }
}

The possible result of the request is the unexpected ClassCastException:

java.lang.ClassCastException: Util cannot be cast to Util
	HelloServlet:doGet(HelloServlet.java:18)
	javax.servlet.http.HttpServlet.service(HttpServlet.			java:617)
	javax.servlet.http.HttpServlet.service(HttpServlet.			java:717)

It means that HelloServlet and Factory classes operate in different context. We have to figure out how these classes were loaded. Let’s use -verbose:class and figure out how the Util class was loaded in regards to HelloServlet and Factory classes.

[Loaded Util from file:/Users/ekabanov/Applications/ apache-tomcat-6.0.20/lib/cl-shared-jar.jar]
[Loaded Util from file:/Users/ekabanov/Documents/workspace-javazone/.metadata/.plugins/org.eclipse.wst. server.core/tmp0/wtpwebapps/cl-demo/WEB-INF/lib/cl-demo- jar.jar]

So the Util class is loaded from two different locations by different classloaders. One is in the web application classloader and the other in the application container classloader.

But why are they incompatible? Turns out that originally every class in Java was identified uniquely by its fully qualified name. But in 1997 a paper was published that exposed an expansive security issue cause by this–namely, it was possible for a sandboxed application (i.e. applet) to define any class includingjava.lang.String and inject its own code outside the sandbox.

The solution was to identify the class by a combination of the fully qualified name and the classloader! It means that Util class loaded from classloader A and Util class loaded from classloader B are different classes as far as the JVM is concerned and one cannot be cast to the other!

The root of this problem is the reversed behavior of the web classloader. If the web classloader would behave in the same way as the other classloaders, then the Util class would have been loaded once from the application container classloader and no ClassCastException would be thrown.

The above example is from this very good article on classloader And below is the pdf version of it.

do-you-really-get-classloaders

get tomcat hot swap work with intellij

It usually takes a lot of time whenever you’re recompiling a Java web project. The IDE would usually recompile the entire project, package them into a war and have it redeployed on your  application server (i.e., Tomcat) then letting the server reinitialize itself. It takes a lot of time, and it just gets worse as your project grows bigger.

Uh, Hot Swap?

Hot swapping is a more efficient process of doing this. Hot swapping works by replacing the classes that you’ve changed instead of having the entire project recompiled. The resulting classes are then replaced (or rather, hot swapped) from the application server, and everything else just runs normally, without the need for a restart since all the other components didn’t have to be reloaded.
Now, Hotswapping isn’t exactly new. Java actually supports it with its own hot swapping solution via HotSpot VM, but it wasn’t exactly usable enough. It’s dealbreaking flaws prevented you from adding new methods or adding new classes. For the most part, that just leaves you to modifying existing method bodies in existing classes. In most cases, that just isn’t enough.
DCEVM fixes that problem. It’s essentially a Java VM modification that allows the redefinition of loaded classes in runtime. This lets you do the points I mentioned above that couldn’t be achieved before.

Getting Hot Swap to Work

This guide demonstrates how to configure Hot Swapping for IntelliJ using DCEVM.

While in a fully-functional IntelliJ Java web project*

  1. Get the updated, forked version of DCEVM here: http://dcevm.github.io/
    • 64-bit is supported!
    • Works in Linux with matching build numbers
    • While unsupported, it’s currently working on Oracle Java JDK versions as well.
  2. Run the package, and choose Install DCEVM as altjvm
  3. In IntelliJ, under your project’s build configurations (Run -> Edit Configurations), make sure that your project uses exploded war artifacts, instead of the normal war packages.
    • The use of exploded war packages allows IntelliJ to update both classes AND resources whenever they are updated. That means this change will also help you in swiftly reloading JSP pages that have been updated.
  4. In the Server tab, add -XXaltjvm=dcevm to the VM options (seems to me this is optional.)
  5. Change On ‘update’ action and On frame deactivation to Update classes and resources.
    • These changes will ensure that IntelliJ will update whenever changes are made and when you’ve shifted the focus away from IntelliJ. If you don’t want to do this automatically, then set Do nothing for these options.
    • If you have the Live Edit plugin, you can tick with Javascript debugger on and all of your changes on JSP files will also reflect as soon as you’ve finished typing. I wouldn’t recommend this since it’ll trigger a refresh every time, but it’s convenient if you want a WYSIWYG experience.
The end result should look like this.
Once you’re done, simply use Debug mode when executing your web application.
Depending on your settings:
  • IntelliJ will make and compile any classes you’ve changed once you’ve switched your focus away from the IDE. Your changes will reflect as soon as you refresh the page.
  • If you’ve used HotSpot VM before (the default Hot swap), you’ll be able to create new classes and methods in both new and existing classes. Give it a try.
  • Frameworks like Spring and Hibernate are likely not fully supported, due to the nature of how they work or how they are brought up (and how it might be improperly done by hot swapping). It’s workable, but you just have to be careful when to modify or add code that relies on these frameworks.
    • If you REALLY need to, then JRebel has support for it. Unfortunately, JRebel costs money and DCEVM being free is the exact reason why I wrote this post.

 

FROM HERE

JVM Architecture

The Architecture of the Java Virtual Machine

Figure below shows a block diagram of the Java virtual machine that includes the major subsystems and memory areas described in the specification. As mentioned in previous chapters, each Java virtual machine has a class loader subsystem: a mechanism for loading types (classes and interfaces) given fully qualified names. Each Java virtual machine also has an execution engine: a mechanism responsible for executing the instructions contained in the methods of loaded classes.

JVM-arch

As each new thread comes into existence, it gets its own pc register (program counter) and Java stack. If the thread is executing a Java method (not a native method), the value of the pc register indicates the next instruction to execute. A thread’s Java stack stores the state of Java (not native) method invocations for the thread. The state of a Java method invocation includes its local variables, the parameters with which it was invoked, its return value (if any), and intermediate calculations. The state of native method invocations is stored in an implementation-dependent way in native method stacks, as well as possibly in registers or other implementation-dependent memory areas.

The Java stack is composed of stack frames (or frames). A stack frame contains the state of one Java method invocation. When a thread invokes a method, the Java virtual machine pushes a new frame onto that thread’s Java stack. When the method completes, the virtual machine pops and discards the frame for that method.

Figure below for a graphical depiction of the memory areas the Java virtual machine creates for each thread. These areas are private to the owning thread. No thread can access the pc register or Java stack of another thread.

java-stack

For heap/method area, read this post

The Program Counter

Each thread of a running program has its own pc register, or program counter, which is created when the thread is started. The pc register is one word in size, so it can hold both a native pointer and areturnAddress. As a thread executes a Java method, the pc register contains the address of the current instruction being executed by the thread. An “address” can be a native pointer or an offset from the beginning of a method’s bytecodes. If a thread is executing a native method, the value of the pc register is undefined.

The Java Stack

When a new thread is launched, the Java virtual machine creates a new Java stack for the thread. As mentioned earlier, a Java stack stores a thread’s state in discrete frames. The Java virtual machine only performs two operations directly on Java Stacks: it pushes and pops frames.

The method that is currently being executed by a thread is the thread’s current method. The stack frame for the current method is the current frame. The class in which the current method is defined is called the current class, and the current class’s constant pool is the current constant pool. As it executes a method, the Java virtual machine keeps track of the current class and current constant pool. When the virtual machine encounters instructions that operate on data stored in the stack frame, it performs those operations on the current frame.

When a thread invokes a Java method, the virtual machine creates and pushes a new frame onto the thread’s Java stack. This new frame then becomes the current frame. As the method executes, it uses the frame to store parameters, local variables, intermediate computations, and other data.

A method can complete in either of two ways. If a method completes by returning, it is said to have normal completion. If it completes by throwing an exception, it is said to have abrupt completion. When a method completes, whether normally or abruptly, the Java virtual machine pops and discards the method’s stack frame. The frame for the previous method then becomes the current frame.

The Stack Frame

The stack frame has three parts: local variables, operand stack, and frame data. The sizes of the local variables and operand stack, which are measured in words, depend upon the needs of each individual method. These sizes are determined at compile time and included in the class file data for each method. The size of the frame data is implementation dependent.

When the Java virtual machine invokes a Java method, it checks the class data to determine the number of words required by the method in the local variables and operand stack. It creates a stack frame of the proper size for the method and pushes it onto the Java stack.

Local Variables

The local variables section of the Java stack frame is organized as a zero-based array of words. Instructions that use a value from the local variables section provide an index into the zero-based array. Values of type int, float, reference, and returnAddress occupy one entry in the local variables array. Values of type byte, short, and char are converted to int before being stored into the local variables. Values of type long and double occupy two consecutive entries in the array.

To refer to a long or double in the local variables, instructions provide the index of the first of the two consecutive entries occupied by the value. For example, if a long occupies array entries three and four, instructions would refer to that long by index three. All values in the local variables are word-aligned. Dual-entry longs and doubles can start at any index.

The local variables section contains a method’s parameters and local variables. Compilers place the parameters into the local variable array first, in the order in which they are declared. Figure 5-9 shows the local variables section for the following two methods:

// On CD-ROM in file jvm/ex3/Example3a.java
class Example3a {

    public static int runClassMethod(int i, long l, float f,
        double d, Object o, byte b) {

        return 0;
    }

    public int runInstanceMethod(char c, double d, short s,
        boolean b) {

        return 0;
    }
}

Figure 5-9. Method parameters on the local variables section of a Java stack.Note that Figure 5-9 shows that the first parameter in the local variables for runInstanceMethod() is of type reference, even though no such parameter appears in the source code. This is the hidden this reference passed to every instance method. Instance methods use this reference to access the instance data of the object upon which they were invoked. As you can see by looking at the local variables for runClassMethod() in Figure 5-9, class methods do not receive a hidden this. Class methods are not invoked on objects. You can’t directly access a class’s instance variables from a class method, because there is no instance associated with the method invocation.

Note also that types byte, short, char, and boolean in the source code become ints in the local variables. This is also true of the operand stack. As mentioned earlier, the boolean type is not supported directly by the Java virtual machine. The Java compiler always uses ints to represent boolean values in the local variables or operand stack. Data types byte, short, and char, however, are supported directly by the Java virtual machine. These can be stored on the heap as instance variables or array elements, or in the method area as class variables. When placed into local variables or the operand stack, however, values of type byte, short, and char are converted into ints. They are manipulated as ints while on the stack frame, then converted back into byte, short, or charwhen stored back into heap or method area.

Also note that Object o is passed as a reference to runClassMethod(). In Java, all objects are passed by reference. As all objects are stored on the heap, you will never find an image of an object in the local variables or operand stack, only object references.

Aside from a method’s parameters, which compilers must place into the local variables array first and in order of declaration, Java compilers can arrange the local variables array as they wish. Compilers can place the method’s local variables into the array in any order, and they can use the same array entry for more than one local variable. For example, if two local variables have limited scopes that don’t overlap, such as the i and j local variables in Example3b, compilers are free to use the same array entry for both variables. During the first half of the method, before j comes into scope, entry zero could be used for i. During the second half of the method, after i has gone out of scope, entry zero could be used for j.

// On CD-ROM in file jvm/ex3/Example3b.java
class Example3b {

    public static void runtwoLoops() {

        for (int i = 0; i < 10; ++i) {
            System.out.println(i);
        }

        for (int j = 9; j >= 0; --j) {
            System.out.println(j);
        }
    }
}

As with all the other runtime memory areas, implementation designers can use whatever data structures they deem most appropriate to represent the local variables. The Java virtual machine specification does not indicate how longs and doubles should be split across the two array entries they occupy. Implementations that use a word size of 64 bits could, for example, store the entire long or double in the lower of the two consecutive entries, leaving the higher entry unused.

Operand Stack

Like the local variables, the operand stack is organized as an array of words. But unlike the local variables, which are accessed via array indices, the operand stack is accessed by pushing and popping values. If an instruction pushes a value onto the operand stack, a later instruction can pop and use that value.

The virtual machine stores the same data types in the operand stack that it stores in the local variables: int, long, float, double, reference, and returnType. It converts values of type byte,short, and char to int before pushing them onto the operand stack.

Other than the program counter, which can’t be directly accessed by instructions, the Java virtual machine has no registers. The Java virtual machine is stack-based rather than register-based because its instructions take their operands from the operand stack rather than from registers. Instructions can also take operands from other places, such as immediately following the opcode (the byte representing the instruction) in the bytecode stream, or from the constant pool. The Java virtual machine instruction set’s main focus of attention, however, is the operand stack.

The Java virtual machine uses the operand stack as a work space. Many instructions pop values from the operand stack, operate on them, and push the result. For example, the iadd instruction adds two integers by popping two ints off the top of the operand stack, adding them, and pushing the int result. Here is how a Java virtual machine would add two local variables that contain ints and store the int result in a third local variable:

iload_0    // push the int in local variable 0
iload_1    // push the int in local variable 1
iadd       // pop two ints, add them, push result
istore_2   // pop int, store into local variable 2

In this sequence of bytecodes, the first two instructions, iload_0 and iload_1, push the ints stored in local variable positions zero and one onto the operand stack. The iadd instruction pops those two int values, adds them, and pushes the int result back onto the operand stack. The fourth instruction, istore_2, pops the result of the add off the top of the operand stack and stores it into local variable position two. In Figure 5-10, you can see a graphical depiction of the state of the local variables and operand stack while executing these instructions. In this figure, unused slots of the local variables and operand stack are left blank.

Figure 5-10. Adding two local variables.

Frame Data

In addition to the local variables and operand stack, the Java stack frame includes data to support constant pool resolution, normal method return, and exception dispatch. This data is stored in theframe data portion of the Java stack frame.

Many instructions in the Java virtual machine’s instruction set refer to entries in the constant pool. Some instructions merely push constant values of type int, long, float, double, or String from the constant pool onto the operand stack. Some instructions use constant pool entries to refer to classes or arrays to instantiate, fields to access, or methods to invoke. Other instructions determine whether a particular object is a descendant of a particular class or interface specified by a constant pool entry.

Whenever the Java virtual machine encounters any of the instructions that refer to an entry in the constant pool, it uses the frame data’s pointer to the constant pool to access that information. As mentioned earlier, references to types, fields, and methods in the constant pool are initially symbolic. When the virtual machine looks up a constant pool entry that refers to a class, interface, field, or method, that reference may still be symbolic. If so, the virtual machine must resolve the reference at that time.

Aside from constant pool resolution, the frame data must assist the virtual machine in processing a normal or abrupt method completion. If a method completes normally (by returning), the virtual machine must restore the stack frame of the invoking method. It must set the pc register to point to the instruction in the invoking method that follows the instruction that invoked the completing method. If the completing method returns a value, the virtual machine must push that value onto the operand stack of the invoking method.

The frame data must also contain some kind of reference to the method’s exception table, which the virtual machine uses to process any exceptions thrown during the course of execution of the method. An exception table, which is described in detail in Chapter 17, “Exceptions,” defines ranges within the bytecodes of a method that are protected by catch clauses. Each entry in an exception table gives a starting and ending position of the range protected by a catch clause, an index into the constant pool that gives the exception class being caught, and a starting position of the catch clause’s code.

When a method throws an exception, the Java virtual machine uses the exception table referred to by the frame data to determine how to handle the exception. If the virtual machine finds a matching catch clause in the method’s exception table, it transfers control to the beginning of that catch clause. If the virtual machine doesn’t find a matching catch clause, the method completes abruptly. The virtual machine uses the information in the frame data to restore the invoking method’s frame. It then rethrows the same exception in the context of the invoking method.

In addition to data to support constant pool resolution, normal method return, and exception dispatch, the stack frame may also include other information that is implementation dependent, such as data to support debugging.

Possible Implementations of the Java Stack

Implementation designers can represent the Java stack in whatever way they wish. As mentioned earlier, one potential way to implement the stack is by allocating each frame separately from a heap. As an example of this approach, consider the following class:

// On CD-ROM in file jvm/ex3/Example3c.java
class Example3c {

    public static void addAndPrint() {
        double result = addTwoTypes(1, 88.88);
        System.out.println(result);
    }

    public static double addTwoTypes(int i, double d) {
        return i + d;
    }
}

Figure 5-11 shows three snapshots of the Java stack for a thread that invokes the addAndPrint() method. In the implementation of the Java virtual machine represented in this figure, each frame is allocated separately from a heap. To invoke the addTwoTypes() method, the addAndPrint() method first pushes an int one and double 88.88 onto its operand stack. It then invokes theaddTwoTypes() method.

Figure 5-11. Allocating frames from a heap.The instruction to invoke addTwoTypes() refers to a constant pool entry. The Java virtual machine looks up the entry and resolves it if necessary.

Note that the addAndPrint() method uses the constant pool to identify the addTwoTypes() method, even though it is part of the same class. Like references to fields and methods of other classes, references to the fields and methods of the same class are initially symbolic and must be resolved before they are used.

The resolved constant pool entry points to information in the method area about the addTwoTypes() method. The virtual machine uses this information to determine the sizes required byaddTwoTypes() for the local variables and operand stack. In the class file generated by Sun’s javac compiler from the JDK 1.1, addTwoTypes() requires three words in the local variables and four words in the operand stack. (As mentioned earlier, the size of the frame data portion is implementation dependent.) The virtual machine allocates enough memory for the addTwoTypes() frame from a heap. It then pops the double and int parameters (88.88 and one) from addAndPrint()‘s operand stack and places them into addTwoType()‘s local variable slots one and zero.

When addTwoTypes() returns, it first pushes the double return value (in this case, 89.88) onto its operand stack. The virtual machine uses the information in the frame data to locate the stack frame of the invoking method, addAndPrint(). It pushes the double return value onto addAndPrint()‘s operand stack and frees the memory occupied by addTwoType()‘s frame. It makesaddAndPrint()‘s frame current and continues executing the addAndPrint() method at the first instruction past the addTwoType() method invocation.

Figure 5-12 shows snapshots of the Java stack of a different virtual machine implementation executing the same methods. Instead of allocating each frame separately from a heap, this implementation allocates frames from a contiguous stack. This approach allows the implementation to overlap the frames of adjacent methods. The portion of the invoking method’s operand stack that contains the parameters to the invoked method become the base of the invoked method’s local variables. In this example, addAndPrint()‘s entire operand stack becomes addTwoType()‘s entire local variables section.

Figure 5-12. Allocating frames from a contiguous stack.This approach saves memory space because the same memory is used by the calling method to store the parameters as is used by the invoked method to access the parameters. It saves time because the Java virtual machine doesn’t have to spend time copying the parameter values from one frame to another.

Note that the operand stack of the current frame is always at the “top” of the Java stack. Although this may be easier to visualize in the contiguous memory implementation of Figure 5-12, it is true no matter how the Java stack is implemented. (As mentioned earlier, in all the graphical images of the stack shown in this book, the stack grows downwards. The “top” of the stack is always shown at the bottom of the picture.) Instructions that push values onto (or pop values off of) the operand stack always operate on the current frame. Thus, pushing a value onto the operand stack can be seen as pushing a value onto the top of the entire Java stack. In the remainder of this book, “pushing a value onto the stack” refers to pushing a value onto the operand stack of the current frame.

One other possible approach to implementing the Java stack is a hybrid of the two approaches shown in Figure 5-11 and Figure 5-12. A Java virtual machine implementation can allocate a chunk of contiguous memory from a heap when a thread starts. In this memory, the virtual machine can use the overlapping frames approach shown in Figure 5-12. If the stack outgrows the contiguous memory, the virtual machine can allocate another chunk of contiguous memory from the heap. It can use the separate frames approach shown in Figure 5-11 to connect the invoking method’s frame sitting in the old chunk with the invoked method’s frame sitting in the new chunk. Within the new chunk, it can once again use the contiguous memory approach.

FROM HERE