From:深入理解Java虚拟机
- 目录
BiBi - JVM -0- 开篇
BiBi - JVM -1- Java内存区域
BiBi - JVM -2- 对象
BiBi - JVM -3- 垃圾收集算法
BiBi - JVM -4- HotSpot JVM
BiBi - JVM -5- 垃圾回收器
BiBi - JVM -6- 回收策略
BiBi - JVM -7- Java类文件结构
BiBi - JVM -8- 类加载机制
BiBi - JVM -9- 类加载器
BiBi - JVM -10- 虚拟机字节码
BiBi - JVM -11- 编译期优化
BiBi - JVM -12- 运行期优化
BiBi - JVM -13- 并发
编译期的三种情况
1)前端编译器:把java文件转变为class文件。
2)JIT编译器:运行期编译器把字节码转变为机器码。
3)AOT编译器:把java文件编译成本地机器码。
对性能的优化主要集中在JIT,这样可以让那些不是由javac产生的class文件【如:Groovy、JRuby】也同样能享受到编译器优化带来的好处。
javac编译的过程
(解析与填充符号表 >< 注解处理) > 分析与字节码生成
public void foo(final int arg) {
final int var = 0;
}
public void foo(int arg) {
int var = 0;
}
上面两段代码编译出来的Class文件是一样的。由于局部变量在常量池中没有CONSTANT_Fieldref_info的符号引用,自然就没有访问标志的信息,所以在Class文件中不可能知道一个局部变量是不是声明为final。
将局部变量声明为final,对运行期是没有影响的,变量的不变性仅仅由编译器在编译期间保障。
语法糖
泛型、变长参数、自动装箱/拆箱、高级for循环、内部类、枚举类、switch支持字符串等,虚拟机运行时不支持这些语法,它们在编译阶段还原回基础的语法结构。
如果用户代码中没有提供任何构造函数,那编译器将会添加一个没有参数的、访问性与当前类一致的默认构造函数,这个工作在【填充符号表】阶段完成。
把字符串的加操作替换为StringBuffer或StringBuilder的append操作。
void say() {
String str = "bb" + "cc" + "dd" + "ee";
str = "aa" + str;
System.out.println(str);
}
对应的Class为:
void say() {
String var1 = "bbccddee";
var1 = "aa" + var1;
System.out.println(var1);
}
void say() {
String[] arr = { "aa", "bb", "cc" };
String res = null;
for (String str : arr) {
res += str;
}
System.out.println(res);
}
对用的Class为:
void say() {
String[] var1 = new String[]{"aa", "bb", "cc"};
String var2 = null;
String[] var3 = var1;
int var4 = var1.length;
for(int var5 = 0; var5 < var4; ++var5) {
String var6 = var3[var5];
var2 = var2 + var6;
}
System.out.println(var2);
}
void say() {
List<String> list = new ArrayList<>();
list.add("ljg");
for (String s : list) {
System.out.println(s);
}
}
对用的Class为:
void say() {
ArrayList var1 = new ArrayList();
var1.add("ljg");
Iterator var2 = var1.iterator();
while(var2.hasNext()) {
String var3 = (String)var2.next();
System.out.println(var3);
}
}
总结:
1)字符串相加优化的场景是有限的,所以在开发中还是要自己主动使用StringBuilder。
2)高级for循环会被转化为基本for循环样式。
3)泛型会被擦除,转换为手动的类型转换
真泛型:C#,在运行期是真实存在的,有自己的虚方法表和类型数据。
伪泛型:Java,在编译后的字节码中会擦除泛型信息,替换为原生类型,并在相应的地方插入强制类型转换。对Java而言,ArrayList<int>和ArrayList<String>是同一个类。
public void method(List<String> list) {
System.out.println(list.toString());
}
public void method(List<Integer> list) {
System.out.println(list.toString());
}
上面的代码不能被编译,因为擦除后两个方法签名一样。
- Integer
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(c == d);//ture
System.out.println(e == f);//falde
System.out.println(c == (a + b));//true
System.out.println(c.equals(a + b));//true
System.out.println(g == (a + b));//true
System.out.println(g.equals(a + b));//false 因为equals()方法不处理数据类型转换
}
- switch字符串的语法糖
public static void main(String[] args) {
String str = "a";
switch (str) {
case "a":
System.out.println("aaa");
break;
case "b":
System.out.println("bbb");
break;
default:
break;
}
}
对应的Class为:
public static void main(String[] var0) {
String var1 = "a";
byte var3 = -1;
switch(var1.hashCode()) {
case 97:
if (var1.equals("a")) {
var3 = 0;
}
break;
case 98:
if (var1.equals("b")) {
var3 = 1;
}
}
switch(var3) {
case 0:
System.out.println("aaa");
break;
case 1:
System.out.println("bbb");
}
}
网友评论