JVM 从入门到精通(六)JVM运行时数据区——虚拟机栈3

简介: JVM 从入门到精通(六)JVM运行时数据区——虚拟机栈3

8.4 方法重写的本质


动态语言和静态语言


1.动态类型语言和静态类型语言两者的区别就在于 对类型的检查是在编译期还是在运行期,满足前者就是静态类型语言,反之是动态类型语言。


2.说的再直白一点就是,静态类型语言是判断变量自身的类型信息;动态类型语言是判断变量值的类型信息,变量没有类型信息,变量值才有类型信息,这是动态语言的一个重要特征。

Java:String info = "mogu blog";       (Java是静态类型语言的,会先编译就进行类型检查)
JS:var name = "shkstart";    var name = 10; (运行时才进行检查)


方法重写的本质


Java 语言中方法重写的本质:


1.找到操作数栈顶的第一个元素所执行的对象的 实际类型,记作C。


2.如果 在类型C中找到与常量中的描述符合简单名称都相符的方法,则进行访问权限校验


1.如果通过则返回这个方法的直接引用,查找过程结束


2.如果不通过,则返回java.1ang.IllegalAccessError 异常


3.否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。


4.如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。


IllegalAccessError介绍


1.程序试图访问或修改一个属性或调用一个方法,这个属性或方法,你没有权限访问。


2.一般的,这个会引起编译器异常。这个错误如果发生在运行时,就说明一个类发生了不兼容的改变。


3.比如,你把应该有的jar包放从工程中拿走了,或者Maven中存在jar包冲突


回看解析阶段


1.解析阶段就是 将常量池内的符号引用转换为直接引用的过程


2.解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT Class info、CONSTANT Fieldref info、CONSTANT Methodref info等


8.5 多态与虚方法表


虚方法表


1.在面向对象的编程中,会很频繁的使用到 动态分派,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能 影响到执行效率。


2.因此,为了提高性能,JVM采用在类的方法区建立一个虚方法表(virtual method table)来实现,非虚方法不会出现在表中。使用索引表来代替查找。


3.每个类中都有一个虚方法表,表中存放着各个方法的实际入口。


4.虚方法表是什么时候被创建的呢?虚方法表会在类加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,JVM会把该类的虚方法表也初始化完毕。


5.如图所示:如果类中重写了方法,那么调用的时候,就会直接在该类的虚方法表中查找



九、方法返回地址


方法返回地址(return address)


1.存放调用该方法的pc寄存器的值。一个方法的结束,有两种方式:


1.正常执行完成


2.出现未处理的异常,非正常退出


2.无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。


3.本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。


4.正常完成出口和异常完成出口的区别在于:通过异常完成出口退出的不会给他的上层调用者产生任何的返回值。


方法退出的两种方式


当一个方法开始执行后,只有两种方式可以退出这个方法


正常退出:


1.执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口;


2.一个方法在正常调用完成之后,究竟需要使用哪一个返回指令,还需要根据方法返回值的实际数据类型而定。


3.在字节码指令中,返回指令包含:


1.ireturn:当返回值是boolean,byte,char,short和int类型时使用


2.lreturn:Long类型


3.freturn:Float类型


4.dreturn:Double类型


5.areturn:引用类型


6.return:返回值类型为void的方法、实例初始化方法、类和接口的初始化方法


异常退出:


1.在方法执行过程中遇到异常(Exception),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,简称异常完成出口。


2.方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码



代码举例


代码

public class ReturnAddressTest {
    public boolean methodBoolean() {
        return false;
    }
    public byte methodByte() {
        return 0;
    }
    public short methodShort() {
        return 0;
    }
    public char methodChar() {
        return 'a';
    }
    public int methodInt() {
        return 0;
    }
    public long methodLong() {
        return 0L;
    }
    public float methodFloat() {
        return 0.0f;
    }
    public double methodDouble() {
        return 0.0;
    }
    public String methodString() {
        return null;
    }
    public Date methodDate() {
        return null;
    }
    public void methodVoid() {
    }
    static {
        int i = 10;
    }
    public void method2() {
        methodVoid();
        try {
            method1();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void method1() throws IOException {
        FileReader fis = new FileReader("atguigu.txt");
        char[] cBuffer = new char[1024];
        int len;
        while ((len = fis.read(cBuffer)) != -1) {
            String str = new String(cBuffer, 0, len);
            System.out.println(str);
        }
        fis.close();
    }
}


方法正常返回


ireturn



dreturn



areturn



异常处理表:


反编译字节码文件,可得到 Exception table


from :字节码指令起始地址


to :字节码指令结束地址


target :出现异常跳转至地址为 11 的指令执行


type :捕获异常的类型



十、相关面试题


1.举例栈溢出的情况?(StackOverflowError)


递归调用等,通过-Xss设置栈的大小;


2.调整栈的大小,就能保证不出现溢出么?


不能 如递归无限次数肯定会溢出,调整栈大小只能保证溢出的时间晚一些,极限情况会导致OOM内存溢出(Out Of Memery Error)注意是Error


3.分配的栈内存越大越好么?


不是 会挤占其他线程的空间


4.垃圾回收是否会涉及到虚拟机栈?


不会



关于Error我们再多说一点,上面的讨论不涉及Exception


首先Exception和Error都是继承于Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。


Exception和Error体现了JAVA这门语言对于异常处理的两种方式。


Exception是java程序运行中可预料的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理。


Error是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。


其中的Exception又分为检查性异常和非检查性异常。两个根本的区别在于,检查性异常 必须在编写代码时,使用try catch捕获(比如:IOException异常)。非检查性异常 在代码编写使,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的。


5.方法中定义的局部变量是否线程安全?


要具体情况具体分析

/**
 * 面试题:
 * 方法中定义的局部变量是否线程安全?具体情况具体分析
 *
 * 何为线程安全?
 *     如果只有一个线程可以操作此数据,则必定是线程安全的。
 *     如果有多个线程操作此数据,则此数据是共享数据。如果不考虑同步机制的话,会存在线程安全问题
 *
 * 我们知道StringBuffer是线程安全的源码中实现synchronized,StringBuilder源码未实现synchronized,在多线程情况下是不安全的 * 二者均继承自AbstractStringBuilder * */
public class StringBuilderTest {
    //s1的声明方式是线程安全的,s1在方法method1内部消亡了
    public static void method1(){
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
    }
    //stringBuilder的操作过程:是不安全的,因为method2可以被多个线程调用
    public static void method2(StringBuilder stringBuilder){
        stringBuilder.append("a");
        stringBuilder.append("b");
    }
    //s1的操作:是线程不安全的 有返回值,可能被其他线程共享
    public static StringBuilder method3(){
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        return s1;
    }
    //s1的操作:是线程安全的 ,StringBuilder的toString方法是创建了一个新的String,s1在内部消亡了
    public static String method4(){
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        return s1.toString();
    }
    public static void main(String[] args) {
        StringBuilder s = new StringBuilder();
        new Thread(()->{
            s.append("a");
            s.append("b");
        }).start();
        method2(s);
    }
}


目录
相关文章
|
1月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
56 0
|
1月前
|
监控 架构师 Java
Java虚拟机调优的艺术:从入门到精通####
本文作为一篇深入浅出的技术指南,旨在为Java开发者揭示JVM调优的神秘面纱,通过剖析其背后的原理、分享实战经验与最佳实践,引领读者踏上从调优新手到高手的进阶之路。不同于传统的摘要概述,本文将以一场虚拟的对话形式,模拟一位经验丰富的架构师向初学者传授JVM调优的心法,激发学习兴趣,同时概括性地介绍文章将探讨的核心议题——性能监控、垃圾回收优化、内存管理及常见问题解决策略。 ####
|
1月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
1月前
|
Java
JVM运行时数据区
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一
28 2
|
2月前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
62 1
|
2月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
488 1
|
3月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
49 4
|
24天前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
2月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80