JVM(详细易懂)

JVM

1.JVM的位置

  • JVM它是运行在在操作系统之上,说白了它就相当于是操作系统上的一个软件。
  • 操作系统又在硬件体系之上,它们相互之间联系。
    在这里插入图片描述

2.JVM体系结构

  • .java文件:java源代码
  • Class File:Java Class文件(字节码文件)
  • 类加载器:通过一个类的全限定名来获取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口,根据字节码在java堆中生成一个代表这个类的java.lang.Class对象。
  • 方法区:属于共享内存区域,存储已经被虚拟机加载的类的信息,常量和静态变量等信息。
  • java虚拟机栈:线程私有,生命周期和线程一致。描述的是Java方法执行的内存模型,每个方法在执行时都会创建一个栈帧,它用于存储局部变量表,操作数栈,动态链接等信息。每一个方法在调用到结束都对应这一个栈帧从虚拟机入栈到出栈的过程。
  • 本地方法栈:为虚拟机使用到的native方法服务。
  • :对于绝大多数应用来说,这里是JVM管理内存中最大的一块。主要是用于存储对象实例和数组。
  • 程序计数器:字节解释器的工作就是通过改变程序计数器的值来选取需要执行下一条指令的字节码指令,分支,循环,跳转等指令都需要依靠程序计数器来执行。
    在这里插入图片描述

3.类加载器

  • 根类加载器:用来加载Java的核心类,它是由底层代码实现的,并不继承与Java.lang.ClassLoader。

  • 扩展类加载器:它通常用来加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。

  • 系统类加载器:他也被称为应用类加载器,他负责加载JVM启动时来自Java命令的-classpath选项和Java.lang.path属性。

4.类加载机制:

  • 全盘负责机制:所谓的全盘负责就是当一个类加载器负责加载某一个类时,该类所依赖或者引用的其他类也都由该类加载器加载。
  • 双亲委派机制:当类加载器接到加载类的任务以后,他会先把这个类交给父加载器去加载,如果父加载器上还有父加载器,则一直往上抛,如果父加载器能够加载,就成功返回;如果无法加载时,才会自己去加载。
  • 缓存机制:所有被类加载器加载的类都会被缓存,当程序中需要使用某个类时,类加载器会先从缓冲区中去找,如果没有才会去通过全限定名来获取字节码文件,最后通过字节码文件在Java堆中生成这个类的对象。

5.native关键字

5.1native出现的背景
  • Java诞生的时候,C++和C横行,要想立足,那必须要有调用C++和C的程序。
5.2存在的意义
  • Java是跨平台语言,既然是跨了平台,所付出的代价就是牺牲对一些底层的控制,而Java要实现对底层的控制,那就需要借助其他语言的帮助,这就是native的作用了,即扩展Java的使用,融合不同语言为Java所用。
  • 凡是代理native关键字的,说明Java的作用范围达不到了,回去调用底层c语言的库。
5.3实现步骤
  • 带有native的方法会先进入本地方法栈(用来登记native方法)
  • 调用本地方法接口,即JNI
  • 在最终执行的时候通过调用本地方法接口(JNI)来加载本地方法库。
    在这里插入图片描述

6.方法区

6.1概念:
  • 方法区存储在加载到内存的类的信息。静态变量,常量。
  • 详细一些来说,方法区存放着类的版本,字段,方法,接口和常量池。
6.2.常量池:
  • 常量池池中存着字面量和符号引用。
6.3.总结:
  • 方法区例存储着Class文件信息和动态常量池,Class文件信息又包含了类信息和Class文件常量池,可以把类信息想成是一个对Class文件内容的框架,里面具体的内容有常量池来进行存储。

  • 动态常量池里的内容除了是静态常量池里的内容外,还将静态常量池里的符号引用转变为直接引用,而且动态常量池里的内容是能动态添加的。

7.栈

7.1什么是栈
  • 栈就是数据结构,它用于存储基本类型变量,引用类型变量和方法。
    在这里插入图片描述
7.2栈的优点
  • 存储速度比堆快,仅次于寄存器,栈数据可以共享。
7.3栈的缺点
  • 存在栈中的数据的大小和生存周期是必须确定的,所以缺乏灵活性。
7.4 栈的工作流程
  • 当在一段代码中定义了一个变量时,Java就在栈中为其分配了内存空间,当变量超过作用域以后,栈就会自动释放掉为其分配的内存空间,另作他用。

8.堆(垃圾回收)

8.1堆中有什么
  • 堆中主要是用来存对象实例,常量,变量,方法,保存我们引用类型的真实对象。
8.2 堆分细分为三个区域
  • 新生区(GC垃圾回收主要是在新生区和养老区)
  • 养老区
  • 永久区
    在这里插入图片描述
8.2.1新生区
  • 类出生,成长甚至死亡的地方。
  • 我们的对象都是在这个区new出来的。
  • 当伊甸园满了之后就会触发一次轻量级的GC回收,死的就直接被回收了,活下来的就进入了幸存区,幸存0区和1区之间是相互交换的。
8.2.2养老区
  • 当新生区(伊甸园和幸存区)满了之后,就会触发一次重量级GC回收,这个时候活下来的就会进入养老区。
8.2.3永久区
  • 这个区域是常驻内存的,用来存放jdk自身携带的class对象,Interface元数据,Java运行时的一些环境,这个区域不存在垃圾回收。关闭JVM就会释放这个区的内存

9.实例化一个对象的过程

  • 实例
//MainApp.java
public class MainApp {
    public static void main(String[] args) {
        Animal animal = new Animal("Puppy");
        animal.printName();
    }
}
//Animal.java
public class Animal {
    public String name;

    public Animal(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println("Animal [" + name + "]");
    }
}
  1. 在编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。
  2. 然后JVM找到AppMain的主函数入口,开始执行main函数。
  3. main函数的第一条命令是Animal animal = new Animal(“Puppy”);就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。、
  4. 加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存,然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。最后会把堆内存中Animal的地址给栈中aninal。
  5. 当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。
  6. 开始运行printName()函数的字节码(可以把字节码理解为一条条的指令)
  • 栈中存在对象的引用变量,通过变量中这个地址,就可以准确的找到我们刚才创建出来的对象,以后我们要使用这个对象做一些事情,调用此对象的方法什么的,都用过这个引用。

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值