• 初始化
    • <clint>是有编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,非必须,没有就不会产生
    • 在子类的<clint>方法执行前父类的一定执行完了,所以第一个执行的肯定是object,父类的赋值语句一定在子类之前
    • 接口无静态初始化语句,所以父类的<clint>不一定先会执行,只有在父类中定义的变量使用时,父接口才会初始化,因此接口的实现类在初始化时也一样不会执行接口的初始化
    • <clint>阻塞执行,线程安全
  • 加载
    • 类和它的加载器一起共同决定是否唯一,同一个类被不同加载器加载,那么则认为是不相等    这个相等包括equal方法、isInstance方法、instanceOf方法的结果
    • 加载器层级
      • 启动类   加载<JAVA_HOME>\lib目录下和-Xbootclasspath参数指定的路径中的,并且文件名能被虚拟机识别的类库加载到虚拟机内存中,如果需要把加载请求交给启动类加载器,直接使用null替代即可
      • 扩展类   加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器
      • 应用程序类    classLoad中的getSystemClassLoad方法的返回值,负责加载用户类路径上指定的类库,如果没有自定义过,这个就是默认的加载器
    • 不是以继承,而是组合的形式复用父加载器的代码
    • 双亲委托加载保证同一
    • 线程上下文加载器,  然后父类加载器可以请求子类加载器去加载类
  • 栈帧
    • 组成:
      • 局部变量表 
        • 线程私有    1.方法参数    2.局部变量
        • 在编译程序代码时,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,因为基本类型大小确定,非基本类型只保存引用,递归爆的是整个栈而不是某个栈帧,死循环也只是重复复用某个slot,栈帧大小不会变化,
        • 基本单位为slot,long和double两个slot(高位对齐),其他为一个    一次long和double数据类型读写切割成两次32位读写,其中任意一个不允许以任何方式单独访问
        • 实例方法(非static)的第0个slot默认存放方法所属对象实例的引用,在方法中可以通过this来访问到这个隐藏的参数
        • 操作数栈可复用,占用大量内存的变量若是出了作用域,之后又刚好有耗时蛮久的操作,该变量所在的slot迟迟不被覆盖,那么gc会认为它仍然存活,所以占用大量内存的变量被认为无用之后及时置null,但适用范围不广
        • 类变量会被至少赋为一个初始值,但局部变量没这待遇
        • 引用至少保证:1.从此引用中直接或间接地查找到对象在java堆中的数据存放的起始地址索引。2.此引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息,即能根据引用找到对象的数据和所属类的类型信息
      • 操作数栈
        • 优化处理:操作数栈之间通过重叠部分进行数据共享
      • 动态链接
      • 方法返回地址
        • 正常返回,给上层返回值
        • 异常返回,出现异常且异常没得到处理,无返回值,系统退出(若异常得到处理,则程序继续执行)
  • 方法调用
    • 方法调用在编译过程中并不连接,只是个符号引用,而不是方法在实际运行期时内存布局中的入口地址,虽然很灵活,但变得复杂,需要在类加载期间,甚至到运行期间才能确定目标方法的直接引用,像c和c++,使用到的类库在编译时要连接,一窝蜂引进来,java可以延迟加载,执行到相应代码才加载类
    • 解析
      • 在类加载阶段就能把部分符号引用转化为直接引用的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。即编译期可知,运行期不可变,代码写好,编译进行编译时就必须确定下来,这种调用叫解析
      • 主要是静态方法和私有方法,无法继承或重写,具体有静态、私有、实例构造器、父类方法,指令为invokestatic和invokespecial   是非虚方法,final修饰的方法指令虽然是invokevirtual,但没有多个版本,所以也认为是非虚
    • 分派
      • 继承和多态的基础,重载和重写的实现    有静态分派   动态分派之分     前者就是重载,后者是重写
      • 重载是根据调用时的参数列表的静态类型来在编译时就确定是具体哪个方法的,在编译时即可确定,动态类型(里面实际放的是父类还是子类)不会影响,但这种确定是根据语言上的规则去理解和推断的,只是"更适合"的版本,根据调用的参数列表在重载方法的参数列表中找个最匹配的,如果不能完全匹配,那么按照优先级来,找最接近的,规则是先自动类型转换,还是不行就装箱,装箱也不行就找装箱类实现的接口,这些接口平级,要是匹配到多个则拒绝编译,还是不行按继承关系往上找
      • 动态分派根据栈顶所指的对象去找,如果找不到沿着继承关系往上找,而不是像动态分派那样,直接根据参数找,而动态分派的参数只是作为检查约束
      • 静态多分派  动态单分派   因为静态分派帮动态分派确定了方法签名了,动态分派只需考虑栈顶是什么对象,而不必去考虑根据实际传过来的参数列表找方法,即静态分派考虑类型还有方法参数,动态分派只考虑类型,不再再次考虑方法参数
      • 稳定优化方法:从父类继承方法表,若覆盖了则方法入口改变,否则不改变
    • 动态语言支持
      • invokedymanic指令
      • 为了解决原有四条"invoke*"指令方法分派规则固化在虚拟机之中的问题,把如何在查找目标方法的决定权从虚拟机转移到具体用户代码中去
posted on 2017-10-25 17:54  一个人的合唱  阅读(118)  评论(0编辑  收藏  举报