Java Virtual Machine (4) notes, Java Virtual Machine notes
In the C language, we want to execute a self-compiled machine command using the following methods:
|
typedef void (*FUNC)( int ); char * str = "your code" ; FUNC f = (FUNC)str; |
That is to say, we can create a tool to read commands from a file and run these commands. In the above Code, "compiled machine commands" refer to running on the CPU. If I implement a translation machine: from translating a custom format instruction to a CPU instruction, you can execute the code according to the custom format. So is the above Code equivalent to the simplest Virtual Machine? The following describes the overall structure of JVM:
ClassLoader is used to load commands that can be recognized by JVM (not just from disk files or memory). First, let's take a look at the format:
The magic number and version will not be mentioned (the file format of manjie is the same), followed by the constant pool, which is nothing more than two:
1. Literal constants (such as Integer, Long, String, etc );
2. symbol reference (where is the method? What is it ?);
As we know, in JVM, classes are all found based on fully qualified names, so the method description should also be the same. Then the relationships between these constants are obtained as follows:
In the next "access permission", it indicates whether the Class is public or private, this & super & interface, on the other hand, shows "this class", "inherited classes", and "Implemented interfaces ", in fact, only the subscript (u2) of CONSTANT_Class_info representing the information is saved here ).
It seems that the NameIndex and DescriptorIndex are a bit like NameAndType. Why not use a NameAndType index directly? The biggest difference between MethodInfo and FieldInfo is Attributes. For example, the attribute table of FieldInfo stores the initial value of the variable, while the Attribute Table of MethodInfo stores the bytecode. Then let's look at these Attributes in sequence, the first is Code:
There are several interesting points:
1. You can know the depth of the stack in the execution process from the Class file;
2. For non-static methods, the compiler will pass this to the method through parameters;
3. The range recorded in the exception table is the number of lines of commands (rather than the source code );
4. The exception here refers to try-catch, while the exception table with the same Code refers to throws;
Exceptions is very simple:
LineNumberTable stores the relationship between bytecode and source code. The structure is as follows:
LocalVariableTable describes the relationship between the variables in the local variable table in the stack frame and the variables defined in the source code. The structure is as follows:
SourceFile specifies the Java source code file name that generates the Class file (for example, many Class files are generated when many classes are declared in a Java file). The structure is as follows:
The Deprecated and Synthetic attributes only have the following differences:
1. Deprecated: it is no longer recommended by the program author. Use @ deprecated to comment out the description;
2. Synthetic: indicates that a field or method is automatically generated by the compiler, for example, <init>;
That's why the Code Attribute is followed by the Attribute?
The loading time of class is very simple: load (nonsense!) when used !). Let's take a look at the process of class loading:
The above process is executed: ClassLoader, which is very important. In JVM, ClassLoader and the Class itself are used to determine whether the two classes are the same. In other words, if different ClassLoader loads the same Class file, the JVM considers the classes they generate to be different. Sometimes the stream is not loaded from the Class file (for example, the Java Applet is loaded from the Network), so the ClassLoader and the general implementation logic are certainly different, different ClassLoader can solve this problem.
However, the use of different ClassLoader triggers a new problem: If I declare a java. lang. Integer, but the code in it is very dangerous, what should I do? The parent-child delegation mode is introduced here:
Except for the top-level start class loaders, all other class loaders should have parent class loaders (implemented through combination ), it delegates the class loading request to the parent class loader first.
In this way, the system class loader will be given priority when loading java. lang. Integer, so that the user's own write will not be loaded. Java programmers see three types of class loaders provided by the system:
1. Bootstrap ClassLoader: loads class libraries in the <JAVA_HOME> \ lib directory and cannot be directly referenced by Java programs;
2. Extension ClassLoader: responsible for loading <JAVA_HOME> \ lib \ ext, which can be directly used by developers;
3. Application ClassLoader: loads the specified class library on ClassPath. If you have not defined your own class loader, it will be used;
In this way, the default class will have the Application ClassLoader to load the class, then, if you find that you want to use a new type, it will recursively use the Application ClassLoader for loading (mentioned in the previous loading process ). In this way, classes can only be loaded using ClassLoader compiled by you in your program, and the loaded class cannot be used by others.
The Parent-parent delegation mode is not a mandatory constraint, but a class loading implementation method recommended by Java designers to developers. Three "damages" in the parent-child delegation mode ":
1. To be compatible with JDK 1.0, we recommend that you override the findClass method;
2. The code for accessing the user class in the basic class may be faulty (such as JNDI): The Class Loader under the thread;
3. user requirements, such as HotSwap and OSGI;
After loading, you need to check how the program runs. Stack frames are used to support method calls and execution by virtual machines. Frames mean a unit. When other methods are called, stack frames are pushed to the stack. The structure is as follows:
After the Class file is compiled, the number of local variables required for running has been determined (as you have seen in the previous Class file ), note that this feature may cause GC (this is not detailed here ). In the stack, the underlying stack always calls the high-level stack (and must be adjacent), so they are passing parameters (return results) usually by pushing it into the operand stack, some virtual machines in order to improve the efficiency of this part of the adjacent stack frame "tangle" together:
Next, let's look at how the method is executed. The first question is which method to execute? Process-oriented programming does not seem to be a problem, but in Java or c ++, this is a problem. The reason is that it is not used normally, but you must understand =. There are two methods for JVM to determine the target method:
1. Static assignment: determines the method to call based on the parameter type and method name. However, it does not mean that an error is returned if no matching type is found, for example, func (int a), but func ('A') is called ') this method will also be called (of course, without func (char a), so the key to giving people is a bit like a processing chain. No matter how complicated it is, these are determined during the compilation process, because it is found up here.
2. Dynamic Allocation: Interface a = new Implements () is the most common. The class of a calling method cannot be determined during compilation. In fact, the implementation of dynamic dispatch is also very simple: Get the actual type of the object before calling the method.
In fact, "static" and "dynamic" give people a vague feeling, and "static assignment" gives people the feeling of finding a method based on the type of parameters, the "Dynamic Allocation" gives people the feeling that they are looking up based on the actual type of the instance. The efficiency of optimizing dynamic allocation for virtual machines is generally to create a virtual method table for the class in the Method Area:
The virtual method table stores the actual entry addresses of each method. If a method is not overwritten in a subclass, the address entry in the virtual method table of the subclass is the same as the address entry in the same method of the parent class, pointing to the implementation entry of the parent class. If the subclass overrides this method, the address in the subclass method table will be replaced with the entry address pointing to the subclass implementation version. In short, it is a preprocessing.
Copyright statement: I feel very grateful if I still write well, I hope you can use your mouse and keyboard to give me a thumbs up or give me a comment! _______________________________________________________________ You are welcome to reprint. I hope to add the original address while you reprint it. Thank you for your cooperation.