当前位置: 首页 > news >正文

吴江网站建设学编程哪个机构好

吴江网站建设,学编程哪个机构好,福田瑞沃小金刚c版,寿光住房和城乡建设局网站文章目录编译期处理默认构造器自动拆装箱泛型集合取值可变参数foreach 循环switch 字符串switch 枚举枚举类try-with-resources方法重写时的桥接方法匿名内部类类加载阶段加载链接初始化相关练习和应用类加载器类与类加载器启动类加载器拓展类加载器双亲委派模式自定义类加载器… 文章目录编译期处理默认构造器自动拆装箱泛型集合取值可变参数foreach 循环switch 字符串switch 枚举枚举类try-with-resources方法重写时的桥接方法匿名内部类类加载阶段加载链接初始化相关练习和应用类加载器类与类加载器启动类加载器拓展类加载器双亲委派模式自定义类加载器拓线程上下文类加载器运行期优化即时编译分层编译方法内联字段优化反射优化编译期处理 所谓的 语法糖 其实就是指 java 编译器把 .java 源码编译为 .class 字节码的过程中自动生成和转换的一些代码主要是为了减轻程序员的负担算是 java 编译器给我们的一个额外福利 注意以下代码的分析借助了 javap 工具idea 的反编译功能idea 插件 jclasslib 等工具。另外 编译器转换的结果直接就是 class 字节码只是为了便于阅读给出了 几乎等价 的 java 源码方式并不是编译器还会转换出中间的 java 源码切记。 默认构造器 public class Candy1 {}经过编译期优化后 public class Candy1 {//这个无参构造器是java编译器帮我们加上的public Candy1() {//即调用父类 Object 的无参构造方法即调用 java/lang/Object. init:()Vsuper();} }自动拆装箱 基本类型和其包装类型的相互转换过程称为拆装箱 在JDK 5以后它们的转换可以在编译期自动完成 public class Demo2 {public static void main(String[] args) {Integer x 1;int y x;} }转换过程如下 public class Demo2 {public static void main(String[] args) {//基本类型赋值给包装类型称为装箱Integer x Integer.valueOf(1);//包装类型赋值给基本类型称谓拆箱int y x.intValue();} }泛型集合取值 泛型也是在 JDK 5 开始加入的特性但 java 在编译泛型代码后会执行 泛型擦除 的动作即泛型信息在编译为字节码之后就丢失了实际的类型都当做了 Object 类型来处理 public class Demo3 {public static void main(String[] args) {ListInteger list new ArrayList();list.add(10); //实际调用的是add(Objcet o)Integer x list.get(0); //实际调用的是get(Object o)} }对应字节码 Code:stack2, locals3, args_size10: new #2 // class java/util/ArrayList3: dup4: invokespecial #3 // Method java/util/ArrayList.init:()V7: astore_18: aload_19: bipush 1011: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;//这里进行了泛型擦除实际调用的是add(Objcet o)14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z19: pop20: aload_121: iconst_0//这里也进行了泛型擦除实际调用的是get(Object o) 22: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; //这里进行了类型转换将Object转换成了Integer27: checkcast #7 // class java/lang/Integer30: astore_231: return所以在取值时编译器真正生成的字节码中还要额外做一些类型转换的操作 //需要将Object转化为Integer Integer x (Integer) list.get(0);如果前面的x变量类型修改为int基本类型则还有自动拆箱的操作 //需要将Object转化为Integer并执行拆箱操作 int x (Integer) list.get(0).intValue();注意 ①擦除的是字节码上的泛型信息可以看到LocalVariableTypeTable仍然保留了方法参数泛型的信息 ②使用反射我们可以获得方法的参数和返回值的泛型信息但是方法内局部变量的泛型信息我们是拿不到的 public SetInteger test(ListString list, MapInteger, Object map) { }Method test Candy3.class.getMethod(test, List.class, Map.class); Type[] types test.getGenericParameterTypes(); for (Type type : types) {if (type instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) type;System.out.println(原始类型 - parameterizedType.getRawType());Type[] arguments parameterizedType.getActualTypeArguments();for (int i 0; i arguments.length; i) {System.out.printf(泛型参数[%d] - %s\n, i, arguments[i]);}} }输出 原始类型 - interface java.util.List 泛型参数[0] - class java.lang.String 原始类型 - interface java.util.Map 泛型参数[0] - class java.lang.Integer 泛型参数[1] - class java.lang.Object可变参数 可变参数也是 JDK 5 开始加入的新特性例如 public class Demo4 {public static void foo(String... args) {//将args赋值给arr可以看出String...实际就是String[] String[] arr args;System.out.println(arr.length);}public static void main(String[] args) {foo(hello, world);} }可变参数 String… args 其实是一个 String[] args 从代码中的赋值语句中就可以看出来。 同 样 java 编译器会在编译期间将上述代码变换为 public class Demo4 {public Demo4 {}public static void foo(String[] args) {String[] arr args;System.out.println(arr.length);}public static void main(String[] args) {foo(new String[]{hello, world});} }注意如果调用的是foo()即未传递参数时等价代码为foo(new String[]{})创建了一个空数组而不是直接传递的null foreach 循环 仍是 JDK 5 开始引入的语法糖数组的循环 public class Demo5 {public static void main(String[] args) {//数组赋初值的简化写法也是一种语法糖。int[] arr {1, 2, 3, 4, 5};for(int x : arr) {System.out.println(x);}} }编译器会帮我们转换为 public class Demo5 {public Demo5 {}public static void main(String[] args) {int[] arr new int[]{1, 2, 3, 4, 5};for(int i0; iarr.length; i) {int x arr[i];System.out.println(x);}} }如果是集合使用foreach public class Demo5 {public static void main(String[] args) {ListInteger list Arrays.asList(1, 2, 3, 4, 5);for (Integer x : list) {System.out.println(x);}} }集合要使用foreach需要该集合类实现了Iterable接口因为集合的遍历需要用到迭代器Iterator public class Demo5 {public Demo5 {}public static void main(String[] args) {ListInteger list Arrays.asList(1, 2, 3, 4, 5);//获得该集合的迭代器IteratorInteger iterator list.iterator();while(iterator.hasNext()) {Integer x (Integer)iterator.next();System.out.println(x);}} }注意 foreach 循环写法能够配合数组以及所有实现了 Iterable 接口的集合类一起使用其中Iterable 用来获取集合的迭代器 Iterator switch 字符串 从 JDK 7 开始switch 可以作用于字符串和枚举类这个功能其实也是语法糖例如 public class Demo6 {public static void main(String[] args) {String str hello;switch (str) {case hello :System.out.println(h);break;case world :System.out.println(w);break;default:break;}} }在编译器中执行的操作 public class Demo6 {public Demo6() {}public static void main(String[] args) {String str hello;int x -1;//通过字符串的hashCodevalue来判断是否匹配switch (str.hashCode()) {//hello的hashCodecase 99162322 ://再次比较因为字符串的hashCode有可能相等if(str.equals(hello)) {x 0;}break;//world的hashCodecase 11331880 :if(str.equals(world)) {x 1;}break;default:break;}//用第二个switch在进行输出判断switch (x) {case 0:System.out.println(h);break;case 1:System.out.println(w);break;default:break;}} }过程说明 在编译期间单个的switch被分为了两个 第一个用来匹配字符串并给x赋值 字符串的匹配用到了字符串的hashCode还用到了equals方法使用hashCode是为了提高比较效率使用equals是防止有hashCode冲突如BM和C. 第二个用来根据x的值来决定输出语句 switch 枚举 public class Demo7 {public static void main(String[] args) {SEX sex SEX.MALE;switch (sex) {case MALE:System.out.println(man);break;case FEMALE:System.out.println(woman);break;default:break;}} }enum SEX {MALE, FEMALE; }编译器中执行的代码如下 public class Demo7 {/** * 定义一个合成类仅 jvm 使用对我们不可见 * 用来映射枚举的 ordinal 与数组元素的关系 * 枚举的 ordinal 表示枚举对象的序号从 0 开始 * 即 MALE 的 ordinal()0FEMALE 的 ordinal()1 */ static class $MAP {//数组大小即为枚举元素个数里面存放了case用于比较的数字static int[] map new int[2];static {//ordinal即枚举元素对应所在的位置MALE为0FEMALE为1map[SEX.MALE.ordinal()] 1;map[SEX.FEMALE.ordinal()] 2;}}public static void main(String[] args) {SEX sex SEX.MALE;//将对应位置枚举元素的值赋给x用于case操作int x $MAP.map[sex.ordinal()];switch (x) {case 1:System.out.println(man);break;case 2:System.out.println(woman);break;default:break;}} }enum SEX {MALE, FEMALE; }枚举类 JDK 7 新增了枚举类以前面的性别枚举为例 enum SEX {MALE, FEMALE; }转换后的代码 public final class Sex extends EnumSex { //对应枚举类中的元素public static final Sex MALE; public static final Sex FEMALE; private static final Sex[] $VALUES;static { //调用构造函数传入枚举元素的值及ordinalMALE new Sex(MALE, 0); FEMALE new Sex(FEMALE, 1); $VALUES new Sex[]{MALE, FEMALE}; }//调用父类中的方法private Sex(String name, int ordinal) { super(name, ordinal); }public static Sex[] values() { return $VALUES.clone(); }public static Sex valueOf(String name) { return Enum.valueOf(Sex.class, name); } }try-with-resources JDK 7 开始新增了对需要关闭的资源处理的特殊语法try-with-resources; try(资源变量 创建资源对象) {} catch() {} 其中资源对象需要实现 AutoCloseable 接口例如 InputStream 、 OutputStream 、 Connection 、 Statement 、 ResultSet 等接口都实现了 AutoCloseable 使用 try-with- resources 可以不用写 finally 语句块编译器会帮助生成关闭资源代码例如 public class Candy9 { public static void main(String[] args) {try(InputStream is new FileInputStream(d:\\1.txt)){ System.out.println(is); } catch (IOException e) { e.printStackTrace(); } } }会被转换为 public class Candy9 { public Candy9() { }public static void main(String[] args) { try {InputStream is new FileInputStream(d:\\1.txt);Throwable t null; try {System.out.println(is); } catch (Throwable e1) { // t 是我们代码出现的异常 t e1; throw e1; } finally {// 判断了资源不为空 if (is ! null) { // 如果我们代码有异常if (t ! null) { try {is.close(); } catch (Throwable e2) { // 如果 close 出现异常作为被压制异常添加t.addSuppressed(e2); } } else { // 如果我们代码没有异常close 出现的异常就是最后 catch 块中的 e is.close(); } } } } catch (IOException e) {e.printStackTrace(); } } }为什么要设计一个 addSuppressed(Throwable e) 添加被压制异常的方法呢 这是为了防止异常信息的丢失想想 try-with-resources 生成的 fianlly 中如果抛出了异常 public class Test6 { public static void main(String[] args) { try (MyResource resource new MyResource()) { int i 1/0; } catch (Exception e) { e.printStackTrace(); } } } class MyResource implements AutoCloseable { public void close() throws Exception { throw new Exception(close 异常); } }输出 java.lang.ArithmeticException: / by zero at test.Test6.main(Test6.java:7) Suppressed: java.lang.Exception: close 异常 at test.MyResource.close(Test6.java:18) at test.Test6.main(Test6.java:6)如以上代码所示两个异常信息都不会丢。 方法重写时的桥接方法 我们都知道方法重写时对返回值分两种情况 父子类的返回值完全一致子类返回值可以是父类返回值的子类 class A { public Number m() { return 1; } } class B extends A { Override // 子类 m 方法的返回值是 Integer 是父类 m 方法返回值 Number 的子类 public Integer m() { return 2; } }对于子类java 编译器会做如下处理 class B extends A { public Integer m() { return 2; }// 此桥接方法才是真正重写了父类 public Number m() 方法 public synthetic bridge Number m() { // 调用 public Integer m() return m(); } }其中桥接方法(也可以叫做合成方法)比较特殊仅对 java 虚拟机可见并且与原来的 public Integer m() 没有命名冲突可以用下面反射代码来验证 public static void main(String[] args) {for(Method m : B.class.getDeclaredMethods()) {System.out.println(m);}}结果 public java.lang.Integer cn.ali.jvm.test.B.m() public java.lang.Number cn.ali.jvm.test.B.m()匿名内部类 源代码 public class Candy10 {public static void main(String[] args) {Runnable runnable new Runnable() {Overridepublic void run() {System.out.println(running...);}};} }转换后的代码 public class Candy10 {public static void main(String[] args) {// 用额外创建的类来创建匿名内部类对象Runnable runnable new Candy10$1();} }// 创建了一个额外的类实现了 Runnable 接口 final class Candy10$1 implements Runnable {public Demo8$1() {}Overridepublic void run() {System.out.println(running...);} }引用局部变量的匿名内部类源代码 public class Candy11 { public static void test(final int x) { Runnable runnable new Runnable() { Override public void run() { System.out.println(ok: x); } }; } }转换后代码 // 额外生成的类 final class Candy11$1 implements Runnable { int val$x; Candy11$1(int x) { this.val$x x; }public void run() { System.out.println(ok: this.val$x); } }public class Candy11 { public static void test(final int x) { Runnable runnable new Candy11$1(x); } }注意 这同时解释了为什么匿名内部类引用局部变量时局部变量必须是 final 的因为在创建Candy11$1 对象时将 x 的值赋值给了 Candy11$1 对象的 val$x 属性所以 x 不应该再发生变化了如果变化那么 val$x 属性没有机会再跟着一起变化 注意上面代码中有些变量中含有$符号这些符号一般会在jvm内部使用方便jvm的操作。java源代码中不会出现这种符号。 类加载阶段 加载 将类的字节码载入方法区1.8后为元空间在本地内存中中其内部采用 C 的 instanceKlass 来描述 java 类它的重要 field 有 _java_mirror 即 java 的类镜像例如对 String 来说它的镜像类就是 String.class作用是把 klass 暴露给 java 使用_super 即父类_fields 即成员变量_methods 即方法_constants 即常量池_class_loader 即类加载器_vtable 虚方法表_itable 接口方法 如果这个类还有父类没有加载先加载父类加载和链接可能是交替运行的 instanceKlass保存在方法区。JDK 8以后方法区位于元空间中而元空间又位于本地内存中_java_mirror则是保存在堆内存中InstanceKlass和*.class(JAVA镜像类)互相保存了对方的地址类的对象在对象头中保存了*.class的地址(也就是说类型指针)。让对象可以通过其找到方法区中的instanceKlass从而获取类的各种信息 注意 ①instanceKlass 这样的【元数据】是存储在方法区1.8 后的元空间内但 _java_mirror 是存储在堆中 ②可以通过前面介绍的 HSDB 工具查看 链接 链接阶段可以分为三个小的步骤 验证验证类是否符合 JVM规范、安全性检查 我们可以试验一下用 UE 等支持二进制的编辑器修改 HelloWorld.class 的魔数在控制台运行结果如下 准备为 static 变量分配空间设置默认值 static变量在JDK 7以前是存储与instanceKlass末尾。但在JDK 7以后就存储在_java_mirror末尾了static变量在分配空间和赋值是在两个阶段完成的。分配空间在准备阶段完成赋值在初始化阶段完成(在类的构造方法中)如果 static 变量是 final 的基本类型以及字符串常量那么编译阶段值就确定了赋值在准备阶段完成如果 static 变量是 final 的但属于引用类型那么赋值也会在初始化阶段完成(在类的构造方法中) 解析将常量池中的符号引用解析为直接引用 符号引用也就是说仅仅是一个符号并不知道这个符号对应着内存的哪一块空间但是经过了解析之后变成了直接引用就知道这些引用的是哪一块位置了。 怎么理解解析这一步骤呢我们可以通过一段代码来看看 我们看看load.C的常量池 我们可以看到JVM_CONSTANT_UnresolvedClass是一个未经解析的类也就是说后面的类D仅仅是一个符号既没有加载也没有解析。 而如果我们使用的是new C(),我们再来看load.C的常量池 可以看到我们的类D已经被解析了拥有了地址。 初始化 初始化阶段就是执行类构造器cinit()V方法的过程虚拟机会保证这个类的『构造方法』的线程安全 cinit()V方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块static{}块中的语句合并产生的 注意 编译器收集的顺序是由语句在源文件中出现的顺序决定的静态语句块中只能访问到定义在静态语句块之前的变量定义在它之后的变量在前面的静态语句块可以赋值但是不能访问如 发生时机 类的初始化的懒惰的以下情况会初始化 main 方法所在的类总会被首先初始化首次访问这个类的静态变量或静态方法时子类初始化如果父类还没初始化会引发子类访问父类的静态变量只会触发父类的初始化Class.forNamenew 会导致初始化 以下情况不会初始化 访问类的 static final 静态常量基本类型和字符串 前面我们说过在类的链接阶段就完成了 类对象.class 不会触发初始化创建该类对象的数组类加载器的.loadClass方法Class.forNamed的参数2为false时 验证类是否被初始化可以看改类的静态代码块是否被执行 验证 实验时先全部注释每次只执行其中一个然后看类的静态代码块是否被执行 public class Load1 {static {System.out.println(main init);}public static void main(String[] args) throws ClassNotFoundException {// 1. 静态常量基本类型和字符串不会触发初始化 // System.out.println(B.b);// 2. 类对象.class 不会触发初始化 // System.out.println(B.class);// 3. 创建该类的数组不会触发初始化 // System.out.println(new B[0]);// 4. 不会初始化类 B但会加载 B、A // ClassLoader cl Thread.currentThread().getContextClassLoader(); // cl.loadClass(cn.ali.jvm.test.classload.B);// 5. 不会初始化类 B但会加载 B、A // ClassLoader c2 Thread.currentThread().getContextClassLoader(); // Class.forName(cn.ali.jvm.test.classload.B, false, c2);// 1. 首次访问这个类的静态变量或静态方法时 // System.out.println(A.a);// 2. 子类初始化如果父类还没初始化会引发 // System.out.println(B.c);// 3. 子类访问父类静态变量只触发父类初始化 // System.out.println(B.a);// 4. 会初始化类 B并先初始化类 A // Class.forName(cn.ali.jvm.test.classload.B);}}class A {static int a 0;static {System.out.println(a init);} } class B extends A {final static double b 5.0;static boolean c false;static {System.out.println(b init);} }相关练习和应用 从字节码分析使用 abc 这三个常量是否会导致 E 初始化 public class Load2 {public static void main(String[] args) {System.out.println(E.a);System.out.println(E.b);// 会导致 E 类初始化因为 Integer 是包装类System.out.println(E.c);} }class E {public static final int a 10;public static final String b hello;public static final Integer c 20;static {System.out.println(E cinit);} }典型应用 - 完成懒惰初始化单例模式 public class Singleton {private Singleton() { } // 内部类中保存单例private static class LazyHolder { static final Singleton INSTANCE new Singleton(); }// 第一次调用 getInstance 方法才会导致内部类加载和初始化其静态成员 public static Singleton getInstance() { return LazyHolder.INSTANCE; } }以上的实现特点是 懒惰实例化初始化时的线程安全是有保障的 类加载器 Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为“类加载器”ClassLoader 类与类加载器 类加载器虽然只用于实现类的加载动作但它在Java程序中起到的作用却远超类加载阶段 对于任意一个类都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性每一个类加载器都拥有一个独立的类名称空间。这句话可以表达得更通俗一些比较两个类是否“相等”只有在这两个类是由同一个类加载器加载的前提下才有意义否则即使这两个类来源于同一个Class文件被同一个Java虚拟机加载只要加载它们的类加载器不同那这两个类就必定不相等 以JDK 8为例 Extension ClassLoader(拓展类加载器)去getParent的时候得到的是个null因为Bootstrap ClassLoader启动类加载器是用C写的不会让我们的Java代码直接访问。 启动类加载器 可通过在控制台输入指令使得类被启动类加器加载 例如 注意 拓展类加载器 如果classpath和JAVA_HOME/jre/lib/ext 下有同名类加载时会使用拓展类加载器加载。当应用程序类加载器发现拓展类加载器已将该同名类加载过了则不会再次加载 其本质就是双亲委派模式的应用 双亲委派模式 双亲委派模式即调用类加载器ClassLoader 的 loadClass 方法时查找类的规则。总的来说就是委派上级来优先做类的加载上级没有再自己来完成类的加载。 要注意这四种类加载器并没有继承的关系只是级别不一样 loadClass源码 protected Class? loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 首先查找该类是否已经被该类加载器加载过了Class? c findLoadedClass(name);//如果没有被加载过if (c null) {long t0 System.nanoTime();try {//看是否被它的上级加载器加载过了 Extension的上级是Bootstarp但它显示为nullif (parent ! null) {c parent.loadClass(name, false);} else {//看是否被启动类加载器加载过c findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader//捕获异常但不做任何处理}if (c null) {//如果还是没有找到先让拓展类加载器调用findClass方法去找到该类如果还是没找到就抛出异常//然后让应用类加载器去找classpath下找该类long t1 System.nanoTime();c findClass(name);// 记录时间sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;} }自定义类加载器 使用场景 想加载非 classpath 随意路径中的类文件通过接口来使用实现希望解耦时常用在框架设计这些类希望予以隔离不同应用的同名类都可以加载不冲突常见于 tomcat 容器 步骤 继承ClassLoader父类要遵从双亲委派机制重写 findClass 方法 不是重写loadClass方法否则不会走双亲委派机制 读取类文件的字节码调用父类的 defineClass 方法来加载类使用者调用该类加载器的 loadClass 方法 破坏双亲委派模式 双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前——即JDK1.2面世以前的“远古”时代 建议用户重写findClass()方法在类加载器中的loadClass()方法中也会调用该方法 双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷导致的 如果有基础类型又要调用回用户的代码此时也会破坏双亲委派模式 双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求而导致的 这里所说的“动态性”指的是一些非常“热”门的名词代码热替换Hot Swap、模块热部署Hot Deployment等 拓线程上下文类加载器 我们在使用 JDBC 时都需要加载 Driver 驱动不知道你注意到没有不写 Class.forName(com.mysql.jdbc.Driver)也是可以让 com.mysql.jdbc.Driver 正确加载的你知道是怎么做的吗 让我们追踪一下源码 先不看别的看看 DriverManager 的类加载器 System.out.println(DriverManager.class.getClassLoader());打印 null表示它的类加载器是 Bootstrap ClassLoader会到 JAVA_HOME/jre/lib 下搜索类但 JAVA_HOME/jre/lib 下显然没有 mysql-connector-java-5.1.47.jar 包这样问题来了在DriverManager 的静态代码中怎么能正确加载 com.mysql.jdbc.Driver 呢 继续看 loadInitialDrivers() 方法 先看 2发现它最后是使用 Class.forName 完成类的加载和初始化关联的是应用程序类加载器因此可以顺利完成类加载 再看 1它就是大名鼎鼎的 Service Provider Interface SPI 约定如下在 jar 包的 META-INF/services 包下以接口全限定名名为文件文件内容是实现类名称 这样就可以使用 ServiceLoader接口类型 allImpls ServiceLoader.load(接口类型.class); Iterator接口类型 iter allImpls.iterator(); while(iter.hasNext()) {iter.next(); }来得到实现类体现的是【面向接口编程解耦】的思想在下面一些框架中都运用了此思想 JDBCServlet 初始化器Spring 容器Dubbo对 SPI 进行了扩展 接着看 ServiceLoader.load 方法 public static S ServiceLoaderS load(ClassS service) {// 获取线程上下文类加载器ClassLoader cl Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl); }线程上下文类加载器是当前线程使用的类加载器默认就是应用程序类加载器它内部又是由Class.forName 调用了线程上下文类加载器完成类加载具体代码在 ServiceLoader 的内部类LazyIterator 中 运行期优化 即时编译 分层编译 JVM 将执行状态分成了 5 个层次 0层解释执行用解释器将字节码翻译为机器码1层使用 C1 即时编译器编译执行不带 profiling2层使用 C1 即时编译器编译执行带基本的profiling3层使用 C1 即时编译器编译执行带完全的profiling4层使用 C2 即时编译器编译执行 profiling 是指在运行过程中收集一些程序执行状态的数据例如【方法的调用次数】【循环的 回边次数】等 即时编译器JIT与解释器的区别 解释器 将字节码解释为机器码下次即使遇到相同的字节码仍会执行重复的解释是将字节码解释为针对所有平台都通用的机器码 即时编译器 将一些字节码编译为机器码并存入 Code Cache下次遇到相同的代码直接执行无需再编译根据平台类型生成平台特定的机器码 对于大部分的不常用的代码我们无需耗费时间将其编译成机器码而是采取解释执行的方式运行另一方面对于仅占据小部分的热点代码我们则可以将其编译成机器码以达到理想的运行速度。 执行效率上简单比较一下 Interpreter(解释器) C1 C2总的目标是发现热点代码hotspot名称的由 来并优化这些热点代码 我们来看一段代码 大致就是每创建1000个对象记一次时间重复200次我们发现在第145次之后创建时间会有一个明显的锐减 这里就使用了一种优化手段称之为【逃逸分析】它会分析这个new Object出来的对象在循环外面是否会被用到、会不会被其它方法所引用结果发现没有。也就是说这个对象不会逃逸外层用不到既然用不到那么就没有必要创建它。 之所以后来消耗的时间这么短是因为JIT进行了逃逸分析之后把对象创建的字节码给替换掉了干脆不创建对象(C2编译器为了优化可能会把原本的字节码改得面目全非)。 可以使用 -XX:-DoEscapeAnalysis 关闭逃逸分析 逃逸分析是C2 即时编译器做出的优化 参考文档 https://docs.oracle.com/en/java/javase/12/vm/java-hotspot-virtual-machine-performance-enhancements.html#GUID-D2E3DC58-D18B-4A6C-8167-4A1DFB4888E4 方法内联 也属于即时编译器优化手段的一种 private static int square(final int i) {return i * i; }System.out.println(square(9));如果发现 square 是热点方法并且长度不太长时会进行内联所谓的内联就是把方法内代码拷贝、粘贴到调用者的位置 System.out.println(9 * 9);还能够进行常量折叠constant folding的优化 System.out.println(81);注意 C是否为内联函数由自己决定Java由编译器决定。Java不支持直接声明为内联函数的如果想让他内联你只能够向编译器提出请求: 关键字final修饰 用来指明那个函数是希望被JVM内联的如 public final void doSomething() { // to do something }总的来说一般的函数都不会被当做内联函数只有声明了final后编译器才会考虑是不是要把你的函数变成内联函数 相关jvm命令 -XX:UnlockDiagnosticVMOptions -XX:PrintInlining 解锁隐藏参数打印inlining 信息 -XX:CompileCommanddontinline,*JIT2.square 禁止某个方法 inlining -XX:PrintCompilation 打印编译信息*JIT2.square *通配任意报名JIT2匹配类square匹配JIT2类中的方法 字段优化 针对成员变量或者是静态成员变量的读写操作优化。 都是通过即时编译器优化生成的IR图来精简最后的机器码在生成机器码前就好像人浏览了一遍代码通过前后关联情况手动消除了一些重复或无效的代码去掉无效代码减少在code cache中存储的机器码的大小节省内存提高程序运行速度。 主要包括 缓存读取、去除重复操作、分支优化、不可达分支消除 对象字段读取优化 优化一缓存读取 优化二去重去不可达分支可达分支优化 对象字段存储优化 同一个字段先后被存储两次两次操作中间没有对该字段的读取操作没有被方法调用或者没有被间接存储到其他字段JVM会消除第一处的冗余赋值指令Volatile同样可以阻止该优化强制属性值实时刷入内存 class Foo {int a 0; // 冗余重复代码被优化IR图中被去除void bar() {a 1; // 冗余重复代码被优化IR图中被去除a 2; // 最终执行的赋值语句} }// 优化后程序 class Foo { void bar() {a 2; // 最终执行的赋值语句} }局部变量死存储优化 与对象的字段值一样包含去除重复或者无效代码优化分支选择。 // 重复代码消除 int bar(int x, int y) {int t x*y; // 重复冗余代码IR图中被去除t xy;return t; }// 分支代码优化 int bar(boolean f, int x, int y) {int t x*y; // 编译后此处为int t 具体赋值操作留到if程序块中if (f)t xy;return t; // 如果走这条路才会对t进行第一行的赋值操作(int t x*y) 并返回 }// 不可达分支 int bar(int x) {if (false) // 不可达分支IR图中不会被编译return x;elsereturn -x; }注意 可能出现异常时无法进行字段优化 反射优化 public class Reflect1 {public static void foo() {System.out.println(foo...);}public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Method foo Demo3.class.getMethod(foo);for(int i 0; i16; i) {foo.invoke(null);}} }foo.invoke 前面 0 ~ 15 次调用使用的是 MethodAccessor 的 NativeMethodAccessorImpl 实现 invoke方法源码 CallerSensitive public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException {if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class? caller Reflection.getCallerClass();checkAccess(caller, clazz, obj, modifiers);}}//MethodAccessor是一个接口有3个实现类其中有一个是抽象类MethodAccessor ma methodAccessor; // read volatileif (ma null) {ma acquireMethodAccessor();}return ma.invoke(obj, args); }会由DelegatingMehodAccessorImpl去调用NativeMethodAccessorImpl NativeMethodAccessorImpl源码 class NativeMethodAccessorImpl extends MethodAccessorImpl {private final Method method;private DelegatingMethodAccessorImpl parent;private int numInvocations;NativeMethodAccessorImpl(Method var1) {this.method var1;}//每次进行反射调用会让numInvocation与ReflectionFactory.inflationThreshold的值15进行比较并使使得numInvocation的值加一//如果numInvocationReflectionFactory.inflationThreshold则会调用本地方法invoke0方法public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {if (this.numInvocations ReflectionFactory.inflationThreshold() !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {MethodAccessorImpl var3 (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());this.parent.setDelegate(var3);}return invoke0(this.method, var1, var2);}void setParent(DelegatingMethodAccessorImpl var1) {this.parent var1;}private static native Object invoke0(Method var0, Object var1, Object[] var2); }//ReflectionFactory.inflationThreshold()方法的返回值 private static int inflationThreshold 15;一开始if条件不满足就会调用本地方法invoke0随着numInvocation的增大当它大于ReflectionFactory.inflationThreshold的值16时就会本地方法访问器替换为一个运行时动态生成的访问器来提高效率 这时会从反射调用变为正常调用即直接调用 Reflect1.foo()
文章转载自:
http://www.morning.yubkwd.cn.gov.cn.yubkwd.cn
http://www.morning.bmmyx.cn.gov.cn.bmmyx.cn
http://www.morning.yrlfy.cn.gov.cn.yrlfy.cn
http://www.morning.rhchr.cn.gov.cn.rhchr.cn
http://www.morning.ptmgq.cn.gov.cn.ptmgq.cn
http://www.morning.lqtwb.cn.gov.cn.lqtwb.cn
http://www.morning.jpwkn.cn.gov.cn.jpwkn.cn
http://www.morning.qkzdc.cn.gov.cn.qkzdc.cn
http://www.morning.wxfgg.cn.gov.cn.wxfgg.cn
http://www.morning.jtmql.cn.gov.cn.jtmql.cn
http://www.morning.xpfwr.cn.gov.cn.xpfwr.cn
http://www.morning.xgzwj.cn.gov.cn.xgzwj.cn
http://www.morning.qkxt.cn.gov.cn.qkxt.cn
http://www.morning.homayy.com.gov.cn.homayy.com
http://www.morning.ujianji.com.gov.cn.ujianji.com
http://www.morning.dmwbs.cn.gov.cn.dmwbs.cn
http://www.morning.clnmf.cn.gov.cn.clnmf.cn
http://www.morning.jfsbs.cn.gov.cn.jfsbs.cn
http://www.morning.mgmqf.cn.gov.cn.mgmqf.cn
http://www.morning.kfclh.cn.gov.cn.kfclh.cn
http://www.morning.glkhx.cn.gov.cn.glkhx.cn
http://www.morning.jfjqs.cn.gov.cn.jfjqs.cn
http://www.morning.gqryh.cn.gov.cn.gqryh.cn
http://www.morning.wpqwk.cn.gov.cn.wpqwk.cn
http://www.morning.rbjf.cn.gov.cn.rbjf.cn
http://www.morning.lylkh.cn.gov.cn.lylkh.cn
http://www.morning.knqck.cn.gov.cn.knqck.cn
http://www.morning.c7498.cn.gov.cn.c7498.cn
http://www.morning.mngyb.cn.gov.cn.mngyb.cn
http://www.morning.zcqbx.cn.gov.cn.zcqbx.cn
http://www.morning.qcymf.cn.gov.cn.qcymf.cn
http://www.morning.kqylg.cn.gov.cn.kqylg.cn
http://www.morning.zympx.cn.gov.cn.zympx.cn
http://www.morning.rlhgx.cn.gov.cn.rlhgx.cn
http://www.morning.zkrzb.cn.gov.cn.zkrzb.cn
http://www.morning.nldsd.cn.gov.cn.nldsd.cn
http://www.morning.pljdy.cn.gov.cn.pljdy.cn
http://www.morning.rghkg.cn.gov.cn.rghkg.cn
http://www.morning.pgzgy.cn.gov.cn.pgzgy.cn
http://www.morning.fwllb.cn.gov.cn.fwllb.cn
http://www.morning.dlhxj.cn.gov.cn.dlhxj.cn
http://www.morning.ymhjb.cn.gov.cn.ymhjb.cn
http://www.morning.dxpzt.cn.gov.cn.dxpzt.cn
http://www.morning.zyrp.cn.gov.cn.zyrp.cn
http://www.morning.ryrpq.cn.gov.cn.ryrpq.cn
http://www.morning.rlbg.cn.gov.cn.rlbg.cn
http://www.morning.kttbx.cn.gov.cn.kttbx.cn
http://www.morning.txltb.cn.gov.cn.txltb.cn
http://www.morning.lffgs.cn.gov.cn.lffgs.cn
http://www.morning.lmknf.cn.gov.cn.lmknf.cn
http://www.morning.dpwcl.cn.gov.cn.dpwcl.cn
http://www.morning.bmlcy.cn.gov.cn.bmlcy.cn
http://www.morning.chmkt.cn.gov.cn.chmkt.cn
http://www.morning.jqrp.cn.gov.cn.jqrp.cn
http://www.morning.wklrz.cn.gov.cn.wklrz.cn
http://www.morning.ktfbl.cn.gov.cn.ktfbl.cn
http://www.morning.qbksx.cn.gov.cn.qbksx.cn
http://www.morning.rkmhp.cn.gov.cn.rkmhp.cn
http://www.morning.rjmd.cn.gov.cn.rjmd.cn
http://www.morning.qxmys.cn.gov.cn.qxmys.cn
http://www.morning.tqdlk.cn.gov.cn.tqdlk.cn
http://www.morning.tcsdlbt.cn.gov.cn.tcsdlbt.cn
http://www.morning.dfltx.cn.gov.cn.dfltx.cn
http://www.morning.hypng.cn.gov.cn.hypng.cn
http://www.morning.jzxqj.cn.gov.cn.jzxqj.cn
http://www.morning.wwwghs.com.gov.cn.wwwghs.com
http://www.morning.pzrnf.cn.gov.cn.pzrnf.cn
http://www.morning.hsklc.cn.gov.cn.hsklc.cn
http://www.morning.pakistantractors.com.gov.cn.pakistantractors.com
http://www.morning.bslkt.cn.gov.cn.bslkt.cn
http://www.morning.ptmch.com.gov.cn.ptmch.com
http://www.morning.jytrb.cn.gov.cn.jytrb.cn
http://www.morning.gkmwk.cn.gov.cn.gkmwk.cn
http://www.morning.gnbtp.cn.gov.cn.gnbtp.cn
http://www.morning.kpygy.cn.gov.cn.kpygy.cn
http://www.morning.mwns.cn.gov.cn.mwns.cn
http://www.morning.dyxlj.cn.gov.cn.dyxlj.cn
http://www.morning.bzfld.cn.gov.cn.bzfld.cn
http://www.morning.xswrb.cn.gov.cn.xswrb.cn
http://www.morning.rjhts.cn.gov.cn.rjhts.cn
http://www.tj-hxxt.cn/news/281318.html

相关文章:

  • 网站根目录权限遵义市住房和城乡建设局网站
  • 嘉兴网站排名优化报大型网站开发项目书籍
  • 沈阳做招聘网站益阳中国网
  • 网站怎么用PS做昨天军事新闻最新消息
  • 爱站工具下载网站dns查询
  • 泗阳城乡建设局网站网站改标题降权
  • 元器件网站开发客户做海报找素材的网站
  • 天河区做网站的公司注册个人订阅号
  • 增城做网站要多少钱深圳鹏洲建设工程有限公司网站
  • 用爬虫做网站国外域名注册价格
  • 网站建设新闻 常识网站没有索引量是什么意思
  • 购物网站建设存在的问题烟台网站制作专业
  • 男装网站模板演示wordpress流量统计放在哪里
  • 浙江住建局官方网站广西互联网推广
  • 介绍自己的做的网站吗百度seo优化策略
  • 网站开发项目意义外贸网站推广平台排名
  • 网站建设方案实验报告公司网站建设请示
  • 2010网站建设管理WordPress代收插件
  • 卡密提取网站怎么做免费全面的seo教程
  • 查企业年报的网站利用百度搜索自己的网站
  • 阿里巴巴网站建设的目的长沙网站建设哪家最好
  • 沈阳网站开发培训多少钱如何在图片上做网站水印图
  • 盐城企业网站制作大连企业推广公司
  • 做网站的作用wordpress付费服务器
  • Python用数据库做网站西安外贸网站开发
  • 用vue做pc端网站好吗微信小程序开发模板网站
  • 成品网站w灬源码在线看网站怎么免费建站
  • 做的最好自考网站是哪个网站名称 规则
  • 网网站基础建设优化知识门户网站系统设计
  • 宁波网站设计方案做网站好公司有哪些