JVM的一次分享

先思考一个问题

我们开发人员编写的Java代码是怎么让电脑认识的

Java文件编译的过程
1.程序员编写的.java文件
2.由javac编译成字节码文件.class:(为什么编译成class文件,因为JVM只认识.class文件)
3.在由JVM编译成电脑认识的文件 (对于电脑系统来说 文件代表一切)
在这里插入图片描述

回顾之前关于jvm的认识

为什么说java是跨平台语言

这个跨平台是中间语言(JVM)实现的夸平台-=
java有JVM从软件层面屏蔽了底层硬件、指令层面的细节让他兼容各种系统
在这里插入图片描述

Jdk和Jre和JVM的区别

  1. JDK:Java开发工具包(Java Development Kit),提供了Java的开发环境和运行环境。
  2. JRE:Java运行环境(Java Runtime Environment),提供了Java运行所需的环境。
  3. JVM : Java虚拟机

jvm

1 JVM运行时数据区

在这里插入图片描述

2 解析JVM运行时数据区

2.1 方法区(Method Area)

① 存放:类的元数据(描述类的信息)、常量池、方法信息(方法数据、方法代码)
② 它有个别命叫Non-Heap(非堆)。当方法区无法满足内存分配需求时,抛出OutOfMemoryError异常。

2.2 Java堆(Java Heap)

① 存放对象实例(数组、对象)
② 堆是jvm区域中最大的一块,在jvm启动时就已经创建完毕
③ java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆”。
④ 无论怎么划分,都与存放内容无关,无论哪个区域,存储的都是对象实例,进一步的划分都是为了更好的回收内存,或者更快的分配内存。
⑤ 如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

2.3 程序计数器(Program Counter Register)

① 程序计数器是一块较小的内存空间,它可以看作是:保存当前线程所正在执行的字节码指令的地址(行号)
② 2.程序计数器 是唯一一个 不会 产生 “内存溢出”的区域。
总结:也可以把它叫做线程计数器

在java中最小的执行单位是线程,线程是要执行指令的,执行的指令最终操作的就是我们的电脑,就是 CPU。在CPU上面去运行,有个非常不稳定的因素,叫做调度策略,这个调度策略是时基于时间片的,也就是当前的这一纳秒是分配给那个指令的。

假如:线程A在看直播
在这里插入图片描述
突然,线程B来了一个视频电话,就会抢夺线程A的时间片,就会打断了线程A,线程A就会挂起
在这里插入图片描述
然后,视频电话结束,这时线程A究竟该干什么?
(线程是最小的执行单位,他不具备记忆功能,他只负责去干,那这个记忆就由:程序计数器来记录)

在这里插入图片描述

2.4 Java虚拟机栈(Java Virtual Machine Stacks)

① 方法在执行的同时,会在虚拟机栈中创建一个栈帧
② 栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
③ 当方法太多时,就可能发生 栈溢出异常StackOverflowError,或者内存溢出异常OutOfMemoryError
在这里插入图片描述

  • 局部变量表:是用来存储我们临时8个基本数据类型、对象引用地址、returnAddress类型。(returnAddress中保存的是return后要执行的字节码的指令地址。)
  • 操作数栈:操作数栈就是用来操作的,例如代码中有个 i =
    6*6,他在一开始的时候就会进行操作,读取我们的代码,进行计算后再放入局部变量表中去
  • 动态链接:假如我方法中,有个 service.add()方法,要链接到别的方法中去,这就是动态链接,存储链接的地方。
  • 出口:出口是什呢,出口正常的话就是return,不正常的话就是抛出异常

2.5 本地方法栈(Native Method Stack)

① 原理和结构与虚拟机栈一致
② 不同点: 虚拟机栈中存放的 jdk或我们自己编写的方法,而本地方法栈调用的 操作系统底层的方法。

3.类的加载

程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。

Jvm执行class文件
在这里插入图片描述

3.1、加载

① 加载指的是将类的class文件读入到内存
② Java类加载器由JVM提供,是所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。

3.2、连接过程

  • 验证 .class 正确性校验,确保加载的类信息符合JVM规范,没有安全方面的问题。主要验证是否符合Class文件格式规范,并且是否能被当前的虚拟机加载处理。
  • 准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
  • 解析:虚拟机常量池的符号引用替换为字节引用过程

3.3、初始化

① 给static变量 赋予正确的值
② 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化

static int num = 10 ; 在连接的准备阶段,会把num=0,之后(初始化阶段)再将0修改为10
这里不包含final修饰的static ,因为final在编译的时候就已经分配了。这里不会为实例变量分配初始化。

4.理解双亲委派模式

4.1 类加载器的介绍

  • 启动(Bootstrap)类加载器
  • 扩展(Extension)类加载器
  • 系统类加载器
  • 自定义加载器
    在这里插入图片描述

4.2 理解双亲委派模式

如果一个类收到了类加载的请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最后到达顶层的启动类加载器,如果父类能够完成类的加载任务,就会成功返回,倘若父类加载器无法完成任务,子类加载器才会尝试自己去加载,这就是双亲委派模式。
在这里插入图片描述
就是每个儿子都很懒,遇到类加载的活都给它爸爸干,直到爸爸说我也做不来的时候,儿子才会想办法自己去加载。

4.3优势

① 通过这种层级关系可以避免类的重复加载,当父亲已经加载了该类的时候,就没有必要子类加载器(ClassLoader)再加载一次。
② 考虑到安全因素
Java核心API中定义类型不会被随意替换,假设通过网路传递一个名为java.lang.Integer的类,通过双亲委派的的模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字类,发现该类已经被加载,并不会重新加载网络传递过来java.lang.Integer.而之际返回已经加载过的Integer.class,这样便可以防止核心API库被随意篡改

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值