JVM角度看方法调用-反射篇

5G NR上行控制信道PUCCH 一、PUCCH概述 PUCCH用于承载上行控制信息,相比LTE,NR PUCCH支持5种不同的格式,按照时域上所占用的符号数量可以分为短格式和长格式两种,如下表所示,短格式占用1-2个符号,可以承载1-2bit信息,长格式占用4-14个符号,可承载大于2bit的信息。NR引入短格式PUCCH的目的是可以缩短HARQ-ACK反馈的时延,长格式仍然是考虑到持续时间长可以保证覆盖。 NR中,考虑到系... 阅读详情


JVM角度看方法调用系列文章:

1.JVM角度看方法调用-开篇

2.JVM角度看方法调用-反射篇

3.JVM角度看方法调用-MethodHandle篇

4.JVM角度看方法调用-性能压测篇(码字…)

5.JVM角度看方法调用-Lambda篇(码字…)

​ 在我们平时开发中常用的方法调用有三种:直接调用、反射调用、MethodHandle调用,这一系列文章就围绕着三种调用方式进行原理剖析和性能分析,本文JDK的版本是1.8。


​ 反射是 Java 基础最重要的特性之一,反射可以动态获取信息以及动态调用对象的方法。但由于反射的性能比较糟糕,更多的是用在框架或者服务的初始化阶段,一个高可用服务,其频繁调用的热点代码是不推荐使用反射机制的。接下来这篇文章主要介绍下反射的原理和其糟糕性能的原因以及怎么如何能对反射调用进行一个优化。

一、反射调用的原理

​ 下面这个例子是典型的利用反射进行方法调用,其中反射主要包括两部分:

  1. Class.getMethod 获取Method对象

  2. method.invoke 反射调用目标方法

public class Animal {
  	/**
     * 小动物可以进行简单的加法运算
     * @param one
     * @param two
     * @return
     */
    public int calculation(int one,int two){
        return one+two;
    }
}


public class ReflectCallDemo {
    public static void main(String[] args)throws Exception{
       test(new Animal());
    }
    
    public static void test(Animal animal)throws Exception{
        Method eatMethod = animal.getClass().getMethod("calculation", int.class, int.class);
        Object result = eatMethod.invoke(animal, 1, 2);
        System.out.println("1+2="+result);
    }
}

​ 其中1.Class.getMethod()获取Method对象,在Class实例第一次获取Method时会缓存这个类型的全部Method方法数组,但是这里采用的是SoftReference<ReflectionData> reflectionData是一个软引用,在系统资源不足时还是会回收掉;而且getMethod(String name, Class<?>… parameterTypes)方法需要在缓存的Method[]中再次根据方法签名进行筛选得到需要的Method。所以我们开发中推荐在程序中显式的缓存Class.getMethod()获取到的Method对象,以避免重复获取的开销。

​ 由此可见反射的性能开销就只剩下2.method.invoke() 反射调用目标方法了,这也是本篇的重点,分析其源码,找到开销点,并设法优化。下面先进行源码分析。

Method.invoke()源码如下:

  public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        // 1.方法修饰符权限检查,如:private方法是不允许在本类外部调用的。可以通过 method.setAccessible(true);取消权限检查。
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
      	// 2. 获取MethodAccessor
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        // 3.执行MethodAccessor.invoke
        return ma.invoke(obj, args);
    }

由上述代码可知Method.invoke()方法中真正执行反射调用的是MethodAccessor。

1.1获取MethodAccessor

​ 在Method中有两个关键属性methodAccessor和root。

public final class Method extends Executable {
  ...
	private volatile MethodAccessor methodAccessor;
	private Method              root;
  ...
}

​ 在Class.getMethod时会调用本地native方法获取对应的Method实例命名为root,但是这个对象并不会将引用直接暴露给调用方,而是会copy一个新的Method返回给调用方

Method copy() {
        if (this.root != null)
            throw new IllegalArgumentException("Can not copy a non-root Method");

        Method res = new Method(clazz, name, parameterTypes, returnType,
                                exceptionTypes, modifiers, slot, signature,
                                annotations, parameterAnnotations, annotationDefault);
        // copyMethod中的root属性就是native返回的Method实例
  			res.root = this;
  			// copyMethod中的methodAccessor就是root中的methodAccessor属性。
        res.methodAccessor = methodAccessor;
        return res;
    }

由上述代码可知同一个root的全部 Method 拷贝都会使用同一份 methodAccessor,methodAccessor在root的第一个Method拷贝执行invoke方法时被赋值。

private MethodAccessor acquireMethodAccessor() {
       
        MethodAccessor tmp = null;
        if (root != null) tmp = root.getMethodAccessor();
        if (tmp != null) {
            methodAccessor = tmp;
        } else {
          	// 当root中的methodAccessor为空时即root的第一个Method拷贝执行invoke方法时创建一个新的MethodAccessor
            tmp = reflectionFactory.newMethodAccessor(this);
          	// 赋值给root.methodAccessor.
            setMethodAccessor(tmp);
        }

        return tmp;
    }

接下来看下ReflectionFactory.newMethodAccessor()方法如何创建一个MethodAccessor实现,首先MethodAccessor有DelegatingMethodAccessorImpl、MethodAccessorImpl、NativeMethodAccessorImpl

  • NativeMethodAccessorImpl 是native版本的MethodAccessor实现最终调用native方法invoke0()实现方法调用
  • MethodAccessorImpl:通过ASM技术动态生成一个MethodAccessorImpl的子类并实现invoke方法,这就是java版本的调用
  • DelegatingMethodAccessorImpl 是一个委派模式的MethodAccessor实现,内部包含一个private MethodAccessorImpl delegate属性,执行invoke方法时委派调用delegate.invoke(),他存在的意义是方便在执行了一定次数后由native版本调用切换到java版本调用。
 public MethodAccessor newMethodAccessor(Method var1) {
   		// 是否初始化,未初始化则初始化 
        checkInitted();
        if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
          // 如果设置了 -Dsun.reflect.noInflation=true则直接利用ASM技术创建一个MethodAccessorImpl的子类作为java版本的MethodAccessor返回
          return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
         // 默认-Dsun.reflect.noInflation=false,返回DelegatingMethodAccessorImpl委派模式的MethodAccessor实现,设置其delegate属性为NativeMethodAccessorImpl(native版本的MethodAccessor)
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;
        }
    }






 private static void checkInitted() {
        if (!initted) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    if (System.out == null) {
                        return null;
                    } else {
                      // -Dsun.reflect.noInflation的默认值是false,如果设置为true则一开始直接使用java版本的MethodAccessor实现,此时首次调用会比较耗时,因为ASM动态创建并加载MethodAccessor实现类比较耗时。              
                        String var1 = System.getProperty("sun.reflect.noInflation");
                        if (var1 != null && var1.equals("true")) {
                            ReflectionFactory.noInflation = true;
                        }
												// -Dsun.reflect.inflationThreshold的默认值是15,代表一个Method.invoke的native版本执行15次以后切换成java版本。
                        var1 = System.getProperty("sun.reflect.inflationThreshold");
                        if (var1 != null) {
                            try {
                                ReflectionFactory.inflationThreshold = Integer.parseInt(var1);
                            } catch (NumberFormatException var3) {
                                throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", var3);
                            }
                        }

                        ReflectionFactory.initted = true;
                        return null;
                    }
                }
            });
        }
    }

1.2 native版本MethodAccessor的实现

首先看下native版本下返回的DelegatingMethodAccessorImpl执行invoke时的实现

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        return this.delegate.invoke(var1, var2);
}

DelegatingMethodAccessorImpl的invoke实现是委派NativeMethodAccessorImpl调用invoke方法

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
  // 如果native版本执行超过 -Dsun.reflect.inflationThreshold的的值,默认值是15,则设置DelegatingMethodAccessorImpl的delegate属性为java版本的MethodAccessor实现,即切换为java版本
        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());
          // 切换java版本
            this.parent.setDelegate(var3);
        }
				// native方法
        return invoke0(this.method, var1, var2);
    }

​ 上述代码可知method.invoke反射方法调用,针对同一个root的Method拷贝对象加起来只要执行sun.reflect.inflationThreshold次调用就会切换成java版本,java版本的优势是在频繁执行后,如果符合优化条件可以被JIT编译进行内联优化提升性能。而native版本完全依赖于native方法NativeMethodAccessorImpl.invoke0()方法,native方法是不能JIT进行内联优化的,所以性能开销较大。

1.3 java版本的MethodAccessor的实现

在设置 -Dsun.reflect.noInflation=true后执行下面例子

public class Animal {
  	/**
     * 小动物可以进行简单的加法运算
     * @param one
     * @param two
     * @return
     */
    public int calculation(int one,int two){
        return (one+two)/0;
    }
}


public class ReflectCallDemo {
    public static void main(String[] args)throws Exception{
       test(new Animal());
    }
    
    public static void test(Animal animal)throws Exception{
        Method eatMethod = animal.getClass().getMethod("calculation", int.class, int.class);
        Object result = eatMethod.invoke(animal, 1, 2);
      	// Thread.sleep(100000);
        System.out.println("1+2="+result);
    }
}

打印异常值如下:
  Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at jvm.jit.ReflectCallDemo.test(ReflectCallDemo.java:13)
	at jvm.jit.ReflectCallDemo.main(ReflectCallDemo.java:7)
Caused by: java.lang.ArithmeticException: / by zero
	at jvm.jit.Animal.calculation(Animal.java:20)
	... 4 more

由此可见sun.reflect.GeneratedMethodAccessor2就是通过ASM技术动态生成的MethodAccessor的java版本实现,这个实现类利用断点无法跟进去,我们将例子中的**//Thread.sleep(100000);**注释放开,然后通过阿里的JVM调试工具Arthas检测当前main的java进程,找到jvm中已加载的GeneratedMethodAccessor2类并对其进行反编译一探究竟。GeneratedMethodAccessor2的代码如下:

/*
 * Decompiled with CFR.
 * 
 * Could not load the following classes:
 *  jvm.jit.Animal
 */
package sun.reflect;

import java.lang.reflect.InvocationTargetException;
import jvm.jit.Animal;
import sun.reflect.MethodAccessorImpl;

public class GeneratedMethodAccessor2
extends MethodAccessorImpl {
    /*
     * Loose catch block
     */
    public Object invoke(Object object, Object[] objectArray) throws InvocationTargetException {
        char c;
        char c2;
        Animal animal;
        block18: {
            if (object == null) {
                throw new NullPointerException();
            }
          	// 将object强转成Animal
            animal = (Animal)object;
          	// 参数长度校验
            if (objectArray.length != 2) {
                throw new IllegalArgumentException();
            }
          	// 基础类型参数拆箱(在method.invoke调用侧会进行装箱)
            Object object2 = objectArray[0];
            if (object2 instanceof Byte) {
                c2 = ((Byte)object2).byteValue();
            } else if (object2 instanceof Character) {
                c2 = ((Character)object2).charValue();
            } else if (object2 instanceof Short) {
                c2 = (char)((Short)object2).shortValue();
            } else if (object2 instanceof Integer) {
                c2 = (char)((Integer)object2).intValue();
            } else {
                throw new IllegalArgumentException();
            }
          	// 基础类型参数拆箱(在method.invoke调用侧会进行装箱)
            object2 = objectArray[1];
            if (object2 instanceof Byte) {
                c = ((Byte)object2).byteValue();
                break block18;
            }
            if (object2 instanceof Character) {
                c = ((Character)object2).charValue();
                break block18;
            }
            if (object2 instanceof Short) {
                c = (char)((Short)object2).shortValue();
                break block18;
            }
            if (object2 instanceof Integer) {
                c = (char)((Integer)object2).intValue();
                break block18;
            }
            throw new IllegalArgumentException();
        }
        try {
          	// 调用animal.calculation((int)c2, (int)c);再将返回值基础类型装箱
            return new Integer(animal.calculation((int)c2, (int)c));
        }
        catch (Throwable throwable) {
            throw new InvocationTargetException(throwable);
        }
        catch (ClassCastException | NullPointerException runtimeException) {
            throw new IllegalArgumentException(super.toString());
        }
    }
}

上述代码可知反射java版本的原理其实就是直接调用。

二、反射调用优

大伙都知道反射调用性能比较低,那接下来主要看下反射性能低在什么地方?是否可以通过代码进行优化使其接近于直接调用?

2.1、为什么反射调用性能低

​ 除了我们在上面源码分析过程中遇到的开销外,Method.invoke方法在调用方也存在隐式的开销,可以在字节码中看到端倪。看下面这个例子及其字节码:

java源代码
public class ReflectCallDemo {
    public static void main(String[] args)throws Exception{
       test(new Animal());
    }

    public static void test(Animal animal)throws Exception{
        Method eatMethod = animal.getClass().getDeclaredMethod("calculation", int.class, int.class);
        Object result = eatMethod.invoke(animal, 1, 2);
        //Thread.sleep(1000000000);
    }
}

javap 反编译后的字节码,重点关注29、35、42、46
  
 0 aload_0
 1 invokevirtual #5 <java/lang/Object.getClass : ()Ljava/lang/Class;>
 4 ldc #6 <calculation>
 6 iconst_2
 7 anewarray #7 <java/lang/Class>
10 dup
11 iconst_0
12 getstatic #8 <java/lang/Integer.TYPE : Ljava/lang/Class;>
15 aastore
16 dup
17 iconst_1
18 getstatic #8 <java/lang/Integer.TYPE : Ljava/lang/Class;>
21 aastore
22 invokevirtual #9 <java/lang/Class.getDeclaredMethod : (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;>
25 astore_1
26 aload_1
27 aload_0
28 iconst_2
29 anewarray #10 <java/lang/Object>    // 创建Object[]
32 dup
33 iconst_0
34 iconst_1
35 invokestatic #11 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;> // 对int类型的第一个参数进行装箱转换为Integer类型
38 aastore
39 dup
40 iconst_1
41 iconst_2
42 invokestatic #11 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;> // 对int类型的第二个参数进行装箱转换为Integer类型
45 aastore
46 invokevirtual #12 <java/lang/reflect/Method.invoke : (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;>  // 调用Method.invoke方法
49 astore_2
50 return

 

​ 由于invoke接口的第二个参数是Object[],所以在进行反射调前会隐式的创建一个Object[]数组用于存放参数,而且会对数组中基本类型的参数进行隐式的装箱转换。

​ 下面我们结合调用方的字节码案例以及上面的源码分析总结下反射性能低的原因。

  1. 创建Object[]数组耗时,装箱转换耗时,触发GC耗时:由于invoke接口的第二个参数是Object[],所以在进行反射调前会隐式的创建一个Object[]数组用于存放参数,这个数组会占用堆内存;Object[]数组中的基础数据类型会被隐式进行装箱转换,装箱过程中创建的包装类对象会占用堆内存。频繁执行Method.invoke方法后会产生GC。

  2. 方法修饰符权限检查:Method.invoke方法在执行时会针对方法修饰符进行权限检查

  3. native版本无法内联:native版本反射调用实现完全依赖于NativeMethodAccessorImpl.invoke0()这个native方法方法,native方法是不能JIT进行内联优化的,所以性能开销较大。

  4. java版本超多态虚方法无法内联:java版本的反射调用实现是利用ASM技术动态创建MethodAccessorImpl的子类GeneratedMethodAccessor*,由于代码中各处反射都要通过Method.invoke()调用到MethodAccessor.invoke(),所以Method.invoke 中的MethodAccessor.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得 Method.invoke() 自身难以被内联到调用方。

    在这里插入图片描述

注:虽然Method 没有子类按道理可以被完全去虚化内联,但是由于Method中的MethodAccessor是动态生成的GeneratedMethodAccessor* 存在众多版本,每个版本的方法体都不一样。Method.invoke()方法其实就是委派MethodAccessor即GeneratedMethodAccessor* 执行invoke方法,所以Method.invoke方法本身也存在很多版本,相当于每个不同的方法它的Method.invoke的方法体都是不同的。综上java版本反射调用的实现能否被内联的关键在于 Method.invoke 方法中对 MethodAccessor.invoke 方法的调用是否可以被去虚化并内联(关于虚方法内联相关可以看1.JVM角度看方法调用-开篇)。

2.2、反射调用性能测试和优化

​ 根据上面的总结我们得到了反射调用性能低的几个原因,然后我们接下来看下那些缺陷可以优化那些是没发避免的。我们分阶段进行优化,每优化一个原因就进行性能测试,看下相较于直接调用的差距。为了证明我们的每个优化操作都是有作用的,我们需要看JIT的内联日志以及GC日志,所以这里我们暂且不用JMH框架进行严谨性能测试,采用手写计时取平均值的方式。后续会专门有一篇文章使用JMH框架针对直接调用、反射调用、MethodHandle调用进行性能测试。

(一)、直接调用
	添加JVM参数:-XX:+PrintGC -XX:+UnlockDiagnosticVMOptions  -XX:-TieredCompilation  -XX:+PrintInlining   
public class TargetCallDemo1 {
    public static void main(String[] args) {
        int count=1;
        long totalTime=0L;
        Animal animal=new Animal();
        while(count<=20){
            long start=System.currentTimeMillis();
            for(int i=0;i<100_000_000;i++){
               test(animal);
            }
            long result=System.currentTimeMillis()-start;
            System.out.println("第"+count+"次执行时长:"+result+"毫秒");
            totalTime=totalTime+result;
            count++;
        }
        System.out.println("反射调用平均时长:"+totalTime/20+"毫秒");
    }

    public static void test(Animal animal){
        animal.calculation(128,129);
    }
}

打印日志:
第1次执行时长:10毫秒 
...
第20次执行时长:0毫秒 
反射调用平均时长:1毫秒

根据GC日志可知,上例中直接调用不会触发GC。

根据JIT内联日志可以知,上例中 animal.calculation(128,129);被成功内联。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjtC47Gh-1653492260409)(/Users/yangxiaofei/Library/Application Support/typora-user-images/image-20220524220142596.png)]

总结:

​ 直接调用刚开始在JIT分层编译尚未完全稳定前会有大概10毫秒作有的耗时,分层编译结束后 animal.calculation(128,129)被JIT内联优化,直接调用耗时可以忽略不计。

(二)、反射调用
	添加JVM参数:-XX:+PrintGC -XX:+UnlockDiagnosticVMOptions  -XX:-TieredCompilation  -XX:+PrintInlining 
public class ReflectCallDemo1 {
    public static void main(String[] args)throws Exception{
        int count=1;
        long totalTime=0L;
        Animal animal=new Animal();
        Method eatMethod = animal.getClass().getDeclaredMethod("calculation", int.class, int.class);
        while(count<=20){
            long start=System.currentTimeMillis();
            for(int i=0;i<100_000_000;i++){
                test(eatMethod,animal);
            }
            long result=System.currentTimeMillis()-start;
            System.out.println("第"+count+"次执行时长:"+result+"毫秒");
            totalTime=totalTime+result;
            count++;
        }
        System.out.println("反射调用平均时长:"+totalTime/20+"毫秒");
    }

    public static void test(Method method,Animal animal)throws Exception{
        method.invoke(animal,128,129);
    }

}

打印日志:
第1次执行时长:902毫秒
...
第20次执行时长:722毫秒
反射调用平均时长:753毫秒

根据GC日志可知,上例中反射调用会触发GC。

根据JIT内联日志可以知,上例中 method.invoke(animal,128,129)方法中MethodAccessor即GeneratedMethodAccessor* 执行invoke方法被成功内联。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4BkFgvlo-1653492260410)(/Users/yangxiaofei/Library/Application Support/typora-user-images/image-20220524222407402.png)]

​ 总结:

​ 上述例子未优化的反射调用有 2.1中描述的1/2/3种缺陷,导致相比于直接调用差距较大,仅依据此例子来看反射调用和直接调用相差750倍之余。

(三)、反射调用优化版本1

​ 首先我们优化下缺陷1.创建Object[]数组耗时,装箱转换耗时,触发GC耗时,优化版本如下,将calculation()方法的入参又128,129修改成2,3。Integer只能缓存[-128,127]直接的Integer对象,128和129不在范围内,在执行Integer.valueOf装箱时每次都会创建新对象。添加-XX:InlineSmallCode=1000000,保证test()方法下整个调用链被全部内联,全部内联后Object[]数据会被判断为不可逃逸,此时JIT会采用栈分配的方式创建Object[]不会占用堆内存。

添加JVM参数:-XX:+PrintGC -XX:+UnlockDiagnosticVMOptions  -XX:-TieredCompilation  -XX:+PrintInlining -XX:InlineSmallCode=1000000
public class ReflectCallDemo1 {
    public static void main(String[] args)throws Exception{
        int count=1;
        long totalTime=0L;
        Animal animal=new Animal();
        Method eatMethod = animal.getClass().getMethod("calculation", int.class, int.class);
        while(count<=20){
            long start=System.currentTimeMillis();
            for(int i=0;i<100_000_000;i++){
                test(eatMethod,animal);
            }
            //Thread.sleep(10000);
            long result=System.currentTimeMillis()-start;
            System.out.println("第"+count+"次执行时长:"+result+"毫秒");
            totalTime=totalTime+result;
            count++;
        }
        System.out.println("反射调用平均时长:"+totalTime/20+"毫秒");
    }

    public static void test(Method method,Animal animal)throws Exception{
        method.invoke(animal,2,3);
    }
  
  
  打印日志:
第1次执行时长:362毫秒
...
第20次执行时长:268毫秒
反射调用平均时长:277毫秒

根据GC日志可知,上例中反射调用未触发GC。

根据JIT内联日志可以知,上例中 method.invoke(animal,2,3)方法中MethodAccessor即GeneratedMethodAccessor* 执行invoke方法被成功内联。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9PFL9Tsn-1653492260410)(/Users/yangxiaofei/Library/Application Support/typora-user-images/image-20220525182620547.png)]

​ 总结:

​ 上述例子优化了1.创建Object[]数组耗时,装箱转换耗时,触发GC耗时这个缺陷,耗时有明显提升,仅依据此例子来看反射调用和直接调用相差270倍之余。

(四)、反射调用优化版本2

​ 在上例的基础上再优化2.方法修饰符权限检查,在创建Method后设置Method.setAccessible(true)取消权限检查。

添加JVM参数:-XX:+PrintGC -XX:+UnlockDiagnosticVMOptions  -XX:-TieredCompilation  -XX:+PrintInlining -XX:InlineSmallCode=1000000
public class ReflectCallDemo1 {
    public static void main(String[] args)throws Exception{
        int count=1;
        long totalTime=0L;
        Animal animal=new Animal();
        Method eatMethod = animal.getClass().getMethod("calculation", int.class, int.class);
        eatMethod.setAccessible(true);//取消权限检查
        while(count<=20){
            long start=System.currentTimeMillis();
            for(int i=0;i<100_000_000;i++){
                test(eatMethod,animal);
            }
            //Thread.sleep(10000);
            long result=System.currentTimeMillis()-start;
            System.out.println("第"+count+"次执行时长:"+result+"毫秒");
            totalTime=totalTime+result;
            count++;
        }
        System.out.println("反射调用平均时长:"+totalTime/20+"毫秒");
    }

    public static void test(Method method,Animal animal)throws Exception{
        method.invoke(animal,2,3);
    }

}
  打印日志:
第1次执行时长:239毫秒
...
第20次执行时长:196毫秒
反射调用平均时长:199毫秒

根据GC日志可知,上例中反射调用未触发GC。

根据JIT内联日志可以知,上例中 method.invoke(animal,2,3)方法中MethodAccessor即GeneratedMethodAccessor* 执行invoke方法被成功内联。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kzOYF7g-1653492260411)(/Users/yangxiaofei/Library/Application Support/typora-user-images/image-20220525184150059.png)]

​ 总结:

​ 上述例子优化了1.创建Object[]数组耗时,装箱转换耗时,触发GC耗时和2.方法修饰符权限检查这两个缺陷,相对于只优化1提升不是很明显,仅依据此例子来看反射调用和直接调用相差200倍之余。

(五)、反射调用优化版本3

​ 在上例的基础上再优化3.native版本无法内联,上述反射调用优化版本1、2中全部都是先native版本实现运行15次后转换为java版本实现,然后运行一段时间后变为热点代码被JIT内联优化。由于native版本向java版本切换的过程中针对DelegatingMethodAccessorImpl.invoke()方法中的delegate,会收集到两个类型的profile(NativeMethodAccessorImpl和GeneratedMethodAccessor*),这会增加JIT内联优化的复杂度,推迟内联优化时间。现添加参数-Dsun.reflect.noInflation=true ,可以关闭反射调用的 Inflation 机制,从而取消委派实现,并且直接使用java版本实现,可以使JIT提前编译完成,也可以做更加激进的优化。

添加JVM参数:-XX:+PrintGC -XX:+UnlockDiagnosticVMOptions  -XX:-TieredCompilation  -XX:+PrintInlining -XX:InlineSmallCode=1000000  -Dsun.reflect.noInflation=true
  
  例子和上例相同只是增加一个参数
  
  
  
  
  打印日志:
第1次执行时长:153毫秒
...
第20次执行时长:132毫秒
反射调用平均时长:135毫秒
  

根据GC日志可知,上例中反射调用未触发GC。

根据JIT内联日志可以知,上例中 method.invoke(animal,2,3)方法中MethodAccessor即GeneratedMethodAccessor* 执行invoke方法被成功内联

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tJUmb2jy-1653492260411)(/Users/yangxiaofei/Library/Application Support/typora-user-images/image-20220525201616863.png)]

​ 总结:

​ 上述例子优化了1.创建Object[]数组耗时,装箱转换耗时,触发GC耗时、2.方法修饰符权限检查、3.native版本无法内联这三个缺陷,相对于只优化1、2提升不是很明显,仅依据此例子来看反射调用和直接调用相差130倍之余。

(六)、MethodHandle的黑魔法

​ 针对上例的场景我们已经将反射调用优化到了极致,相比于直接调用仍然相差较大,接下来我们换成MethodHandle试试。

添加JVM参数:-XX:+PrintGC -XX:+UnlockDiagnosticVMOptions  -XX:-TieredCompilation  -XX:+PrintInlining -XX:InlineSmallCode=1000000  -Dsun.reflect.noInflation=true
public class ReflectCallDemo1 {
   static  MethodHandle methodHandle;
   static {

       try {
           Animal animal=new Animal();
           Method eatMethod = animal.getClass().getMethod("calculation", int.class, int.class);
           MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
           methodHandle= publicLookup.unreflect(eatMethod).bindTo(animal);
       } catch (Throwable e) {
           e.printStackTrace();
       }
    }

    final static  MethodHandle finalMethodHandle=methodHandle;

    public static void main(String[] args)throws Throwable{
        int count=1;
        long totalTime=0L;
        while(count<=20){
            long start=System.currentTimeMillis();
            for(int i=0;i<100_000_000;i++){
                test();
            }
            //Thread.sleep(10000);
            long result=System.currentTimeMillis()-start;
            System.out.println("第"+count+"次执行时长:"+result+"毫秒");
            totalTime=totalTime+result;
            count++;
        }
        System.out.println("反射调用平均时长:"+totalTime/20+"毫秒");
    }

        public static void test()throws Throwable{
            int aa= (int) finalMethodHandle.invokeExact(2, 3);
        }

}


 打印日志:
第1次执行时长:12毫秒
...
第20次执行时长:0毫秒
反射调用平均时长:1毫秒

Amazing!!!!!,MethodHandle调用的性能竟然和直接调用不相上下。关于MethodHandle性能为什么直逼直接调用?为什么MethodHandle必须被final修饰?在超多态虚方法调用时MethodHandle性能是否能超越直接调用?这些问题再下一篇3.JVM角度看方法调用-MethodHandle篇中会有讲解。

(七)、java版本超多态虚方法无法内联问题

​ 我们上面的全部优化案例中只优化了反射调用性能低的缺陷1.创建Object[]数组耗时,装箱转换耗时,触发GC耗时、2.方法修饰符权限检查、3.native版本无法内联这三个缺陷。始终没有提到关于4.java版本超多态虚方法无法内联,这是因为我们为了案例简单上述场景中只涉及Animal.calculation(int one,int two)这一个Method的高频调用,所以就不存在java版本的MethodAccessor实现即GeneratedMethodAccessor* 执行invoke方法时无法被内联的问题了,说白了就是没踩到这个坑。下面我们看下在测试程序前增加一下其他类型Method执行invoke方法,创造一下超多态的情况。

public class ReflectCallDemo1 {
    public static void main(String[] args)throws Exception{
        int count=1;
        long totalTime=0L;
        Animal animal=new Animal();
        // 添加其他类型的Method
        polluteProfile();
        Method eatMethod = animal.getClass().getMethod("calculation", int.class, int.class);
        eatMethod.setAccessible(true);//取消权限检查
        while(count<=20){
            long start=System.currentTimeMillis();
            for(int i=0;i<100_000_000;i++){
                test(eatMethod,animal);
            }
            //Thread.sleep(10000);
            long result=System.currentTimeMillis()-start;
            System.out.println("第"+count+"次执行时长:"+result+"毫秒");
            totalTime=totalTime+result;
            count++;
        }
        System.out.println("反射调用平均时长:"+totalTime/20+"毫秒");
    }

    private static void polluteProfile()throws Exception{
        Method method1 = String.class.getMethod("toString");
        Method method2 = String.class.getMethod("trim");
        Method method3=Integer.class.getMethod("intValue");
        String str="123";
        Integer number=123;
        for(int i=0;i<300_000_000;i++){
            method1.invoke(str);
            method2.invoke(str);
            method3.invoke(number);
        }
    }

    public static void test(Method method,Animal animal)throws Exception{
        method.invoke(animal,2,3);
    }

}


 打印日志,明显时间加长了:
第1次执行时长:1085毫秒
...
第20次执行时长:1068毫秒
反射调用平均时长:1053毫秒

  
根据GC日志可知,上例中反射调用会触发GC。

根据JIT内联日志可以知,上例中  method.invoke(animal,2,3)方法中MethodAccessor即GeneratedMethodAccessor* 执行invoke方法未被成功内联。此处内联失败就是因为MethodAccessor.invoke()这个调用点上MethodAccessor存在四个类型,是超多态情况无法去虚化导致的内联失败。
  注:HotSpot针对虚方法进行去虚化优化的缓存profile收集条数(由JVM参数 -XX:TypeProfileWidth 控制)默认是2


接下来当我们修改下polluteProfile()方法,注释掉两个方法调用

 private static void polluteProfile()throws Exception{
        Method method1 = String.class.getMethod("toString");
        String str="123";
        for(int i=0;i<300_000_000;i++){
            method1.invoke(str);
        }
    }

此时在执行上述例子,输出结果如下,明显时间又降下来了:

第1次执行时长:198毫秒
...
第20次执行时长:162毫秒
反射调用平均时长:169毫秒
  

根据GC日志可知,上例中反射调用未触发GC。

根据JIT内联日志可以知,上例中 method.invoke(animal,2,3)方法中MethodAccessor即GeneratedMethodAccessor* 执行invoke方法被成功内联。由于HotSpot针对虚方法进行去虚化优化的缓存profile收集条数(由JVM参数 -XX:TypeProfileWidth 控制)默认是2,所以此处可以进行条件去虚化并且内联。此处的GeneratedMethodAccessor2和GeneratedMethodAccessor3分别代表String.toString()的实现和Animal.calculation()的实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCtReVnd-1653492260411)(/Users/yangxiaofei/Library/Application Support/typora-user-images/image-20220525213929697.png)]

Method.invoke()方法被JIT条件去虚化内联后的伪代码如下:

 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 ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
           
        // 开始
        if(obj.getClass()==String.class){
         		//...省略GeneratedMethodAccessor2中的拆箱
           return (String)obj.toString();
        }else if(obj.getClass()==Animal.class){
          //...省略GeneratedMethodAccessor3中的拆箱
          return (Animal)obj.calculation((int)args[0],(int)args[1]);
        }else{
           return ma.invoke(obj, args);
        }   
 
    }
(八)、总结

​ 反射的前三种缺陷我们还可以通过代码优化解决,但是第四种也是最致命的一个缺陷,它是没办法避免的。所有的反射调用都要对应一个Method实例,所有处于热点代码的Method实例在执行invoke时都会产生一个MethodAccessor类型的实现即GeneratedMethodAccessor* 。所以Method.invoke 中的MethodAccessor.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得 Method.invoke() 自身难以被内联到调用方,进而影响热点代码的性能。

​ 所以当我们热点代码需要用到反射调用时不妨尝试用MethodHandle代替。

​

【数字IC刷题】华为海思数字IC笔试题(3)详细解析版 阅读详情

相关推荐

23. STM32F103C8T6 ADC采集光敏电阻传感器模块光照强度实战

本文详细介绍了如何使用STM32F103C8T6的ADC模块采集光敏电阻传感器的光照强度。通过硬件接线、核心原理讲解、四步代码配置(GPIO、ADC模式、使能校准、数据滤波)以及串口验证,手把手指导完成从模拟信号到数字值的完整实战流程,并重点提示了ADC时钟分频等关键配置要点。

weixin_42300144的博客 219

MethodHandle与反射Method区别,invokedynamic指令

MethodHandle与反射Method区别MethodHandle的使用方法和效果上与Reflection都有众多相似之处。不过,它们也有以下这些区别: Reflection和MethodHandle机制本质上都是在模拟方法调用,但是Reflection是在模拟Java代码层次的方法调用,而MethodHandle是在模拟字节码层次的方法调用。在MethodHandles.Lookup上的三个方

yushui的笔记本 7204

【Q1/论文复现】:利用 CHARLS 数据库考察抗高血压药物依从性和认知衰退

摘要: 北京大学团队基于CHARLS数据(2011-2018)的纵向研究发现,抗高血压药物高依从性能显著延缓中国45岁及以上社区中老年人的认知衰退。研究表明,坚持服药者7年内全局认知衰退速度降低0.014SD/年,记忆功能衰退减缓0.023SD/年,效果接近正常血压人群水平。研究还提示社会经济地位(SES)可能通过影响用药依从性间接调控认知衰老进程。该成果为高血压管理对认知保护提供了人群级证据,发表于《BMC Medicine》(DOI:10.1186/s12916-025-03949-8)。

dingdianbangni的博客 808

java反射运用及优化

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

lejian的专栏 1077

EPICS Phoebus手册1

EPICS客户端phoebus 手册

yuyuyuliang00的博客 3948

微信小程序 - [wxapplib]] backgroundfetch privacy fail {“errno”:101,“errMsg”:“private_getBackgroundFetchDa

微信小程序报错之backgroundfetch privacy fail,微信小程序报错WAServiceMainContext.js:1 [wxapplib]] backgroundfetch privacy fail {“errno“:101,怎么解决?问题出现:调用wx.chooseMessageFile报错(开发者工具中正常)用户隐私协议中未声明小程序使用此功能,安卓真机调试报错:backgroundfetch privacy fail ,怎么解决?微信小程序[wxapplib]] backgrou

🏆 前端领域优质创作者 1万+

Java通过反射新增方法_JDK1.7新增的MethodHandle方法与反射调用的例子

java中通过类调用类的方法之前一直使用反射的机制去做,但是jdk1.7中增加了对动态语言的支持,增加了MethodHandle方法MethodHandle的Demo新建一个类ClassA,方法printlnpublic class ClassA {public void println(String s) {System.out.println(s);}}method handle方法使用pri...

weixin_42298817的博客 695

文末含资料链接和视频讲解!YOLOv8+RepVGG+QueryDet小目标检测终极指南:从理论到实战,突破遥感图像检测难题

QueryDet(Query Detection)机制代表了目标检测领域的一个重要范式转变。传统的目标检测方法基于anchor或anchor-free的密集预测,需要为图像中的每个可能位置生成预测。而QueryDet采用了稀疏查询的方式,通过少量的可学习查询向量来直接预测目标。 这种设计灵感来源于Transformer架构中的注意力机制。在自然语言处理中,Transformer通过query-key-value机制实现了高效的序列建模。QueryDet将这一思想引入到目标检测中,将检测问题转化为查询匹配问

FJN110的博客 384

《深入理解JVM》9-如何实现反射

在本系列的第8节,我们总结了一下jvm怎么执行方法调用,是在类中维护一个方法表,指向的是方法的实际内存地址。查找内存地址的执行时间不通,可以划分为静态绑定和动态绑定。那么反射是怎么执行的呢 public void invokeTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class deClazs = Demo.class; Me

David_lou的专栏 407

复现基于混频因子模型的GDP即时预测方法

基于混频因子模型的GDP即时预测方法复现 本文复现了混频因子模型(MIDAS)在GDP即时预测中的应用。该方法整合7个月度宏观经济变量(工业增加值、CPI、PMI等)与季度GDP数据,通过状态空间模型实现高频预测。技术实现包括:1)使用PCA提取潜在共同因子;2)构建状态空间模型处理混频数据;3)通过卡尔曼滤波进行参数估计。实证结果显示模型预测效果良好(测试集R²=0.82),其中工业增加值对因子1贡献最大(0.82),CPI对因子2贡献显著(0.78)。该方法为决策者提供了更及时的季度GDP估算。

huanghm88的专栏 600

【ComfyUI】SDXL + 单LoRA 房屋平面图渲染装修

本文介绍了一种基于ComfyUI的室内平面设计图自动生成效果图的工作流。该流程通过导入房屋平面图,结合SDXL室内设计模型与北欧风格LoRA模块,快速生成多房间效果展示。工作流包含图像输入、文本处理、模型加载和图像生成等环节,支持风格化渲染和细节补充,适用于建筑设计、家装展示、房地产营销等场景。核心采用SDXL通用室内设计模型和北欧极简LoRA,通过节点衔接实现自动化批量生成,能在短时间内将平面方案转化为逼真的三维效果图。该技术可提升设计呈现效率,辅助客户决策,并应用于教学研究领域。

Mr数据杨 3641

Java MethodHandle 的使用

Java的MethodHandle提供了一种高效、轻量级且类型安全的方法来动态调用Java方法,比反射性能更好

u011345634的博客 2802

Python 爬虫实战:豆瓣 / IMDB 电影数据抓取(评分 + 剧情 + 演员阵容结构化处理)

在数据为王的时代,电影数据是进行市场分析、推荐系统构建和学术研究的重要资源。豆瓣和 IMDB 作为国内外最具影响力的电影评分和资讯网站,包含了海量高质量的电影数据。本文将带领你从零开始,构建一个健壮的 Python 爬虫,用于抓取豆瓣和 IMDB 的电影信息,并将其中的非结构化数据(如剧情简介、演员列表)进行清洗和结构化处理,最终存储到便于分析的结构化文件(如 CSV)中。

yansideyucsdn的博客 953

JVM角度看方法调用-开篇

文章目录一、虚方法和非虚方法1.1、Interpreter下执行比较1.2、JIT下比较1.2.1、内联缓存1.2.2、去虚化和方法内联1)非虚方法内联2)完全去虚化内联3)”陷阱“导致去优化4)条件去虚化内联二、Interpreter、JIT、AOT 分析三、JVM中可以内联和不能内联的场景 JVM角度看方法调用系列文章: 1.JVM角度看方法调用-开篇 2.JVM角度看方法调用-反射篇 (码字...) 3.JVM角度看方法调用-MethodHandle篇(码字...) 4.JVM角度看方法调用-

yangxiaofei_java的博客 1093

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

JVM角度看方法调用-MethodHandle篇

JVM角度看方法调用系列文章:1.JVM角度看方法调用-开篇2.JVM角度看方法调用-反射篇3.JVM角度看方法调用-MethodHandle篇4.JVM角度看方法调用-性能压测篇(码字…)5.JVM角度看方法调用-Lambda篇(码字…)​ 在我们平时开发中常用的方法调用有三种:直接调用、反射调用、MethodHandle调用,这一系列文章就围绕着三种调用方式进行原理剖析和性能分析,本文JDK的版本是1.8。在上篇文章中我们最后总结由于反射调用的种种缺陷严重影响性能,所以当我们热点代码需要用到反射调用时不

yangxiaofei_java的博客 1587

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

从JVM角度看对象创建过程和内存布局

从JVM角度看对象创建过程和内存布局

欢迎每一位朋友不吝赐教,无论是宝贵的建议还是中肯的批评,都将是我前进路上不可或缺的灯塔 1556

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

JVM之反射

平台无关性 编译时,把源码编译成字节码 写一个java代码,利用javac生成class文件,可以利用javap进行反汇编,生成虚指令(字节码) public class com.kaola.zkTest.ByteCodeSample { public com.kaola.zkTest.ByteCodeSample(); Code: 0: aload_0 ...

hucuoshi8718的博客 193

反射,jdk1.8新特性,JVM面试题

JAVA分为编译期和运行期**编译期:**是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作.**运行期:**是把=编译后的文件交给计算机==执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。JAVA反射机制是在运行过程中,对于任意一个类,都可以知道这个类的所有属性和方法;即反射机制指的是程序在运行时能够动态获取自身的信息。

Waaaaangzi的博客 939

从JVM角度看继承

可以看到,无论是默认构造器,还是用户定义构造器,均先调用父类的构造函数ObjectL0ALOAD 0。

qq_40662424的博客 470

Java面试篇【JVM】常见面试题(2024最新)

线程私有:程序计数器,虚拟机栈,本地方法栈线程共享的:堆,方法区,直接内存。

系统看你学习很认真,随机送了一个月会员! 1966

反射和注解

文章目录反射Class类动态加载小结访问字段获取字段值设置字段值小结调用方法调用方法调用静态方法调用非public方法多态小结调用构造方法小结获取继承关系获取父类的Class获取interface继承关系小结动态代理小结注解使用注解注解的作用小结定义注解元注解@Target@Repeatable@Inherited如何定义Annotation小结处理注解使用注解小结 反射 什么是反射? 反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。 正常情况下,如果我们要调用一个对

Java721的博客 239

java反射知识,结合注解

java反射相关基础知识,同时结合注解说明反射和注解重要性

weixin_42233642的博客 1139

JVM运行时数据区——方法区

栈、堆、方法区的交互关系、设置方法区大小、OOM、方法区的内部结构、方法区的演进细节、方法区的垃圾回收

码说芯语的博客 1806

细说Java反射机制

文章目录编译期和运行期Java的类加载类加载器反射反射常用的API获取类的字节码对象获取构造方法获取成员变量获取成员方法反射的优点反射的练习通过反射修改配置文件的内容通过反射越过泛型检查通过反射写一个通用的设置某个对象的某个属性为指定的值 编译期和运行期 假如我们写了一个Demo.java,并要打印hello world那它需要经过哪些步骤? 第一步:通过编译器进行编译,从Java源码 —> Java 字节码 这个编译器则是jdk 里的javac 编译器,我们只需 javac Demo.java 即

weixin_45082647的博客 478

别再用懒汉模式了——从JVM的角度看单例模式

网上结论: 我们先来看看网上普遍的结论: 所谓“懒汉式”与“饿汉式”的区别,是在与建立单例对象的时间的不同。 “懒汉式”是在你真正用到的时候才去建这个单例对象 “饿汉式是在类创建的同时就已经创建好一个静态的对象,不管你用的用不上,一开始就建立这个单例对象 先不说结论,看看下文 代码实现: 饿汉式 public class Singleton1 { private final stat...

小宝的博客 499

深入理解JVM(三)—JVM内存划分

一、JVM运行时数据区域 二、程序计数器 线程私有 当前线程所执行字节码的行号指示器,控制程序执行顺序,分支,循环,跳转。 三、虚拟机栈 线程私有 JVM栈是程序运行时单位,决定了程序如何执行,或者说数据如何处理。 Java栈内存,以帧的形式存放本地方法的调用状态,包括方法调用的参数、局部变量、中间结果等(方法都是以方法帧的形式存放在方法区的),每调用一个方法就将对应该方法的方法帧压入Java ...

LJJZJ的博客 320
上一篇: JVM角度看方法调用-开篇
下一篇: JVM角度看方法调用-MethodHandle篇
躺平程序猿
博客等级 码龄8年 4870粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

躺平程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值