This article deals with some JVM principles and Java bytecode Directives, recommending readers of interest to read a classic book about the JVM, "deep into Java Virtual Machine (2nd edition)", and compare it with the IL assembler directives that I introduced in the. NET 4.0 Object-oriented programming ramble. I believe the reader will have some inspiration. A careful comparison of the similarities and differences of two similar things is one of the most effective ways to learn.
1 Peculiar program output
A while ago, a student showed me a "very weird" Java code:
public class Testinteger {
public static void Main (string[] args) {
Integer v1=100;
Integer v2=100;
System.out.println (V1==V2); Output: True
Integer w1=200;
Integer w2=200;
System.out.println (W1==W2); Output: false
}
}
What confuses the student most is why such a similar code would have such an unexpected output?
I usually use a lot of C#,java, at first see this code output, I also very strange: how can this? Are the two integer values, 100 and 200, inherently different from the integer type?
To understand the underlying cause of the above phenomenon, I used the JAVAP tool to disassemble the. class file generated by the Java compiler:
By carefully reading the Java compiler's native bytecode, I found the following statement to assign a value to an integer variable:
Integer v1=100;
The Integer.valueof method is actually called.
The following statement, which completes the comparison of two integer variables:
System.Console.WriteLine (v1 = = v2);
The IF_ACMPNE directive is actually generated. Where a represents "address", CMP stands for "Compare", and NE stands for "Not equal".
This directive means: compare the two operands in the Java method stack (that is, V1 and v2) to see if they are pointing to the same object in the heap.
When assigning a value of 100 to V1 and V2, they will reference the same integer object.
Then why is the value changed to 200, W1 and W2 "Turn off" and refer to different integer objects respectively?
The secret lies in the Integer.valueof method. Fortunately, the Java Class Library is open source, so we can see the source code without any difficulty:
public static Integer valueOf (int i) {
if (i >= -128 && i <= integercache.high)
Return integercache.cache[i + 128];
Else
return new Integer (i);
}
The truth is, the original integer uses a private static class Integercache internally, which encapsulates an integer object's cache array to buffer the integer object with the following code:
private Static Class Integercache {
Static final Integer cache[];
......
}
Take a closer look at the code inside the Integercache, and you'll see that it uses static initialization blocks to save a total of 256 integer objects within the [-128,127] interval in the cache array.
When an integer value is assigned directly to an integer variable, if the value is within [-128,127], the JVM (Java Virtual machine) uses the integer object cached in the cache directly, otherwise the JVM will recreate an integer object.
All the truth.
2 further exploration of integer
Let's take a closer look at this interesting integer:
Integer v1 = 500;
Integer v2 = 300;
Integer Addresult = v1 + v2; Results: 800
Double Divresult = (double) v1/v2; Results: 1.6666666666666667
Yo, incredibly integer object support subtraction Operation Yes! How did it do that?
Using JAVAP to Disassemble. class files Again is not difficult to find:
Inside the integer class is a field value of the private int type, which represents the integer value "encapsulated" by the integer object.
private final int value;
When V1+V2 is required, the JVM invokes the Intvalue method of the V1 and v2 two integer objects to take out the integer value encapsulated within it, and then calls the iadd instruction directly supported by the JVM to add the two integers directly, and the result is returned to the method stack. Then call the Integer.valueof method to convert to an integer object, and let the Addresult variable refer to the object.
In addition to the rule, the JVM calls the I2D directive to convert int to double, and then calls the ddiv instruction to complete the work of dividing the floating-point number.
From the above analysis, we can know that the integer class itself does not support subtraction, but instead, the Java compiler translates these subtraction statements into bytecode directives that the JVM can execute directly (such as the Iadd and ddiv used in this example), where many of the statements used for type conversion are added.
This shows that using an integer object to subtraction directly compared to the original data type int will result in lower performance and should be avoided.
3 "Curved" design scheme for integer in JDK
Now, we stand at a higher angle and discuss the design of the integer.
I personally think that adding an "object buffer" to an integer type is not a good design, from the first example code you will feel that this design has brought some confusion to the application layer's code. In addition, we see that the JDK designer only caches [-128,127] A total of 256 integer objects, and he may think that the integers within this interval are the most common, so it should be cached to improve performance. As far as I can see, this is a bit too "self-righteous", saying that the number of integers in this interval is used by most of the basis? For applications that often deal with the integer value of >128, this cache is useless and a nuisance. Even if you really want to cache it, it's best to be implemented by the application developer himself, because he can cache the objects that are actually used based on the actual situation he's developing, without having to carry this big burden of 256 integer objects.
And as we saw earlier, subtraction based on an integer object would add many unnecessary types of conversion instructions, far less efficient and more reliable than using the original data type directly.
In fact, the most used is not an integer object but it encapsulates a bunch of static methods (these methods provide common functions such as type conversion), I wonder how many occasions in real development need to create a large number of integer objects, but also assume that they encapsulate the value is also located in [-128,127 ] within the range?
Caching an integer object also poses some risk to multithreaded applications because multiple threads may have access to the same cached integer object at the same time. However, the JDK designer has taken this into account, and I see that the fields of the integer class are final, immutable and immutable, so they can be accessed securely in a multithreaded environment. Although the use of the problem, but it is not a bit curved around? Remove this object cache, is the integer type "lighter" and "better"?
4 C # int challenges Java Integer
It is interesting to compare the design of Java with the design of. NET, in C # as an example.
Java divides the data types into "raw data type" and "Reference data type" categories, int is the original data type, in order to provide developers with some common functions (such as converting a string to int), so the JDK provides a reference type integer, encapsulates these features.
. NET is different, its data types are divided into "value type" and "Reference data type" two broad categories, int is a value type, itself has a rich method, see the following C # code:
int i = 100;
String str = i.ToString (); The int variable itself has a "bunch" of methods
Use. NET Disassembler ildasm look at the IL directives generated by the above code, it is not difficult to discover that the C # compiler will map the int type to the SYSTEM.INT32 structure:
Note that System.Int32 is a value type that lives in the thread stack, in general, a variable with a value type is often more secure in a multithreaded environment than a variable of a reference type because it reduces the problem of multithreaded access to the same object.
=================================
A brief explanation: Please compare the following two methods:
void Dosomethingwithvaluetype (int value);
void Dosomethingwithreferencetype (MyClass obj);
When multiple threads execute both methods at the same time, the thread function uses the value type argument value to be more secure, without worrying about multiple threads interacting with each other, but the obj parameter of the reference type should be careful, if the obj parameter received by multiple threads might refer to the same MyClass object, To ensure that the result is correct, it may be necessary to lock the object.
====================================
As with the JVM. NET CLR also provides special instructions such as add to complete the subtraction function.
From a developer's point of view, the int of C # has special instructions at the virtual machine level, like the original data type int of Java, with some of the features of the Java wrapper class integer, and avoids the quirky features of the integer in Java, which, in my opinion, The int in C # is better and easier to use than the Int/integer in Java.
But from the inside of the exploration technology is very different, Java uses an integer class to "fix" all the common integer processing functions, and for. NET SYSTEM.IN32 structure, curious friends may wish to use reflector to look at the relevant source code, will find System.Int32 in many places in the internal use of the function of the number class, but also use the NumberFormatInfo (provide digital format information) , CultureInfo (to provide current cultural information) and other related types, if the addition of a bunch of interfaces, it is really "quite" complex.
Compared to the Java platform and the. NET platform, it is often found that there are fewer Java packages in many places.
From the point of view of application development, there are many places where Java is less useful than. Net. Taking the very common integer types and their operations involved in this article, I believe we all see that using Java programming requires attention to this "Intege object Cache" trap. NET is very attentive to these discovered traps (. NET designers say: Of course there will be no traps, but that's none of my business) are covered with a "thick" manhole cover, so that developers are very worry, resulting in high development efficiency and a better development experience.
But on the other hand, Java's JDK code at a glance, is open, you have to explore its technical insider, always very convenient, this is still more reassuring.
. NET is relatively closed, always hide, want to list its Lushan truth is not easy, and I feel it for developers too thoughtful, service too good, this is not necessarily a good thing. Because one of the weaknesses of human nature is "indolent", life is too comfortable, enterprising spirit will be a lot less. NET developers can easily unknowingly develop the "bad habit" of technology superficial understanding, because since the code works, then why bother to inquisitive? But then again, if only satisfied with the knowledge, how can the technical progress and improvement? By the time they get older, they are eliminated by the young. And the appearance of this phenomenon, in the end should blame Microsoft, blame the surrounding environment, or own it?
"Reprint" The difference between the int of C # and the Java integer