关于jvm的一些理解(这个是个人猜测,不一定准确,主要是为了方便个人理解)

jvm内存理解——1

  • jvm本身是一个为了执行java代码而创建的一个虚拟计算机,它其实是一个进程,我们的代码就是在这个进程中以线程的形式运行。比如当程序运行时,就会启动一个jvm实例,如果启动多个java程序就会产生多个jvm实例,也就是说,并不是所有的java程序都共享一个jvm虚拟机,他们之间是隔离起来的。当jvm运行时他会首先执行main方法,main方法就是一个线程,而且是主线程,并且其他线程都是由main启动的。main线程是非守护线程,守护线程是jvm虚拟机自己使用的,只有当所有的非守护线程都终止时,jvm才退出。

jvm运行某个类时的执行流程

  • jvm在执行某个类时,必须经过加载,链接,初始化这三个阶段。在初始化阶段,jvm才会开始执行代码。

    • 加载:

      • jvm通过这个类的全限定名来加载该类所对应的二进制字节流文件(.class文件),比如cn.itcast.domain.Person,jvm会根据这个名字解析出它在硬盘中的目录结构,然后找到该文件,把它加载到内存。

      • 将这个字节流所代表的静态数据结构转化为方法区的运行时数据结构,但是jdk1.8之后移除了方法区概念,用元空间代替,元空间是直接的内存空间,并不属于jvm所属的内存空间。其中static成员变量和常量池移到了堆中,方法区其他的内容在元空间中

      • 在堆中生成一个代表这个类的java.lang.Class对象,作为方法区(jdk1.8之后为元空间但是static成员变量和常量池,成员方法和静态方法的字节码在堆中,而且方法只有一份在堆中,除了这些其余内容仍在元空间中)这个类的各种数据的访问入口,所有需要访问和使用类数据只能通过这个Class对象。这个加载的

        过程需要类加载器参与。

    • 链接:链接又分为三个阶段(验证,准备,解析)

      • 验证:这个阶段主要确保被加载类的正确性,就是说来验证这个类是否对jvm有危害
      • 准备:这个阶段主要为类变量分配内存和设置初始初始值,这些内存都在方法区(1.8之后在堆中,静态方法也在堆中)分配。但只给类变量(static)分配实例变量不分配,实例变量主要随着对象的实例化一起分配到堆中。
      • 解析:这个阶段主要是jvm将常量池中的符号引用转化为直接引用的过程
    • 初始化:

      • String str1=“abc”;
        String str2=new String(“abc”);
        编译阶段 str1,str2,"abc"保存在类常量池中,加载阶段 str1,str2在方法区(jdk1.8之后在堆中)的运行时常量池中,而"abc"在堆中的字符串常量池中,在解析阶段str1,str2这些引用符号直接转化为直接符号也就是地址,str1转化为堆中"abc"的地址,而str2在运行时才转化为堆中new出的Sting对象的地址,但是new出来的String对象其实保存的是字符创常量池中"abc"的地址,而字符串常量池中保存的也是地址,因为java字符串在底层是以byte【】的形式保存的,所以字符串常量池中保存的其实是堆中与之对应的byte【】的地址!!!

堆中存储的数据

  • 常量池
  • 符号引用:就是一个类引用了其他的类,但是jvm并不知道引入的类来自哪里,所以就用唯一的符号来代替,当类加载器去加载的解析的时候,就把符号引用转化为那个类的地址。
  • new出来的对象(其中方法不在该区域,该区域只保存了方法的地址)
  • 类方法(和对象虽然都在堆里但不在一块区域)

元空间

  • 懵逼。。。。。

对象.方法的内存理解

  • 当通过一个对象.方法调用方法时,会先找到该方法对应的在堆中的内存空间,对象的堆中会保存变量,虚拟方法表的地址(如果有继承关系的话jvm会在编译时创建这个表,主要保存子类重写父类的方法地址),当调用重写方法时会通过推中的地址然后找到虚拟方法表,再在虚拟方法表中找到对应的方法,虚拟方法表中保存的是该方法的实际地址,然后通过该地址找到方法进栈执行。如果调用的不是重写方法,直接在堆中拿到该方法的地址执行.

继承加载顺序

  • 首先加载子类class文件,当加载子类的class文件时,发现有继承关系,所以会先加载父类的class文件。所以说,父类的class文件先于子类的class文件加载,当父类的class文件加载完后,然后加载子类的class文件,因为为子类的构造方法默认调用父类的构造方法,所以调用父类的构造方法并在子类对象空间对父类变量进行初始化,然后在子类对象空间对自己的变量进行初始化。

多态问题

  • Person p = new Student();p.eat(); 这就是一个多态的体现,父类引用指向子类对象。如果子类重写了父类的方法。当执行上面的语句时,首先会在内存中为Student开辟一块内存空间,然后首先在该空间对父类变量进行初始化,再对自己的变量初始化,然后再把该内存地址赋值给p,但是由于p是父类的引用,所以p只能调用该内存空间中,属于父类的那一部分,当执行p.eat方法时,由于子类重写了父类的eat方法,当调用eat时会现在父类空间里找,找到之后,会查找虚拟表,如果发现虚拟表中找到了说明被重写了,所以会从虚拟表中找到重写后的方法地址执行重写后的方法。

多态详细

  • Person p = new Student();虽然当在运行的时候,new的确实是子类的空间,但是其实在编译时,是父类的引用,jvm也懒,并不会说当运行的时候发现是子类对象然后,自动把父类引用转为子类引用。而是还是通过把它作为父类引用来用。所以当调用p.属性或者p.非重写方法的时候,jvm会去查询父类Person所对应的字节码文件中的属性表和方法表,找到其所对应的偏移值,然后用p所指向的首地址加上偏移量得到实际的内存地址。所以当你用p去调用属性或者非重写方法时,调用的是父类的,因为它查的其实还是父类的字节码文件。当你调用重写方法时,他会查询生成的虚拟方法表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值