Java虚拟机笔记

1、JDK&JRE&JVM

  • Java 执行流程

  • JRE的应用

  • JDK
  • JDK+JRE+JVM

    • JDK(Java开发环境):JRE+工具(编译器、调试器、其他工具等)+类库

      编译器:将Java 文件编译为class文件,也是JVM能运行解释的文件

    • JRE(Java 运行环境):JVM+Java解释器

    • JVM:Java虚拟机

2、Java字节码文件结构

  • 官方字节码文件结构
  • 什么是u2u4

u2:代表数据占2个字节, u4:代表4个字节。

  • JDK编译对应版本号

JDK7-->51 JDK8-->52 JDK-->53 ......

  • 编译的本质

    编译的本质就是Java源文件转换为JVM认识的16进制class文件格式。JVM虚拟机不只是为Java服务,只要符合class标准格式的16进制JVM就能识别。

3、类的加载机制

3.1 类加载大概流程

  1. 装载:获取类的全限定类名,将class文件转化为二进制流。

    1. 将二进制流中类的描述信息 存入方法区。如:创建时间、版本等......

    2. 将java.lang.Class对象放入内存。

  1. 链接

    1. 验证:验证被加载类的正确:如文件的格式,文件元素等。

    2. 准备:在方法区中为静态变量分配空间,并设置初值。

    3. 解析:把类的符号引用转换为直接引用。

3.初始化

为类的静态变量设置默认值,执行静态代码块。

①装载---->②链接(验证,准备,解析)---->初始化

第一步:加载指的是把class字节码文件从各个来源通过**类加载器(包括启动类加载器,拓展类加载器、应用类加载器、用户自定义加载器)**装载入内存中。

流程是,获取全限定类名-->将class字节文件转化为二进制流-->将二进制流中类的描述信息放入方法区。将java.lang.Class对象放入堆内存。

第二步:链接

  • 验证:比如文件格式 的验证,元数据的验证 (如是否继承了final修饰的类),字节码的验证 (保证语义的准确性),符号引用的验证。

  • 准备:主要为静态变量(类变量)分配内存,并赋予初值(初值,不是代码中具体写的初始化的值)。

比如8种基本类型 的初值,默认为0;引用类型 的初值则为null;常量的初值即为代码中设置的值,final static tmp = 456, 那么该阶段tmp的初值就是456

  • 解析:符号引用替换为直接引用的过程。

第三步:初始化

主要是对类变量初始化,是执行类构造器的过程。

拓展:为什么会有自定义类加载器?

  1. 一方面是由于java代码很容易被反编译,如果需要对自己的代码加密的话,可以对编译后的代码进行加密,然后再通过实现自己的自定义类加载器进行解密,最后再加载。

  2. 另一方面也有可能从非标准的来源加载代码,比如从网络来源,那就需要自己实现一个类加载器,从指定源进行加载。

3.2 类加载器

3.2.1 分类

不同的类加载器加载不同的类:

启动类加载器(Bootstrap classloader):主要负责加载Java中的一些核心类库,主要位于<JAVA_HOME>/lin/rt.jar中。

拓展类加载器(Extension classloader):主要加载一些拓展类,是启动类加载类的子类。

应用类加载器:(System classlzhuyoader):主要用于加载CLASSPATH 路径下,我们自己定义的类,是拓展类加载器的子类。

这由于双亲委派机制模型导致的一个原因。

3.2.2 双亲委派模型

如果一个类加载器收到了类的加载请求,他不会自己去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在其父类加载器,则进一步加载向上委托,一次递归,请求最终达到顶层的启动类加载器返回可以完成类加载任务,就成功返回。倘若父类加载器无法完成此次加载任务,子加载才会尝试自己去加载。

  • 如何打破双亲委派机制? 例子:Tomcat

    自定义类加载器类,继承Classloader类,重写loadClass()方法.

4、JVM内存模型

4.1 什么是JVM内存模型

JVM需要使用计算机的内存,Java程序运行中所处理的对象或者算法都会用使用JVM的内存空间,JVM将内存划分为5块,这样的结构称为JVM内存模型。

4.2 JVM为什么进行内存区域划分

随着对象数量的增加,JVM内存使用率也在增加,如果JVM内存使用率达到100%,则无法继续运行Java程序。为了让JVM内存,我们需要对JVM内存进行回收。为了提高垃圾回收效率,JVM将内存区域进行了划分。

4.3 JVM内存划分

JVM按照线程是否独享将内存首先分为两大类。

  • 线程独享区

只有当前线程能访数据的区域,线程之间不能共享。

线程独享区随线程的创建,随线程的销毁而被回收。

  • 线程共享区

    所有线程都可以访问的区域。

当线程被销毁的时候,线程共享区的数据不会立即回收,需要等待达到垃圾回收的阈值之后才会进行回收

  • 模型图

栈内存独立:基本数据类型

堆内存共享:引用数据类型数据

4.3.1 程序计数器

程序计数器会记录当前线程要执行指令的内存地址,只占用一小部分的内存区域,只记录一个地址,所以我们认为程序计数器是不会出现内存溢出的问题的分区。

所以虚拟机是不会优化程序计数器区域的。

4. 3.2 本地方法栈

Java中有些代码的视线是依赖于其他非Java语言的(C++),本地方法栈存储的是维护非Java语句执行过程中产生的数据,一般本地方法栈不会出现内存问题。

4.4 虚拟机栈

存放当前线程中所有声明的变量,包基本数据类型和引用数据类型的引用。

  • 基本数据类型和引用数据类型的划分标准

    • 基本数据类型:变量在声明的时候,能够确认占用内存的大小。

    • 引用数据类型:变量在声明的时候不知道占用内存大小。

      引用数据类型将值的引用放到虚拟机栈中,而对象则存在对内存中,引用数据类型占用4个字节存放地址。

4.4.1 栈帧的结构

  • 常量池的应用中的常量存放的你是值。而是 方法区中常量池中的常量的地址。
  • 局部变量:存放当前方法的局部变量,基本数据类型存储值。引用数据类型存储堆内存中的地址。

  • 操作数栈:对方法的变量提供计算的区域。

  • 常量数据的引用:常量数据会存放到方法区的常量池,不管是基本数据类型还是引用数据类型都会存放常量池的地址。

  • 方法返回值的地址:方法返回数据会存到计算机内存的寄存器中。

相关推荐
小白要加油哈23 分钟前
游戏开发--C#面试题
开发语言·笔记·学习·microsoft·unity·c#·游戏引擎
gsgbgxp43 分钟前
C++类中的const成员变量和const成员函数
开发语言·c++·算法
hiddenSharp42944 分钟前
【Python】使用 爬虫/GitHub Actions 自动更新 CSDN 个人主页浏览量统计
爬虫·python·github
皮克斯的进化之路1 小时前
RabbitMQ的死信队列
java·开发语言
thinkerCoder1 小时前
mac上如何安装指定版本的python
python·科技·macos
好喜欢吃红柚子1 小时前
【卷积基础】CNN中一些常见卷积(1*1卷积、膨胀卷积、组卷积、深度可分离卷积)
人工智能·python·深度学习·计算机视觉·cnn
唐僧洗头爱飘柔95271 小时前
(Go语言)初上手Go?本篇文章帮拿捏Go的数据类型!
开发语言·golang·go语言·go数据类型·go开发·go初上手
唐僧洗头爱飘柔95271 小时前
(Go基础)变量与常量?字面量与变量的较量!
开发语言·后端·golang·go·go语言初上手
·云扬·2 小时前
Lambda 表达式详解
java·开发语言·笔记·学习·1024程序员节
linhhanpy2 小时前
自制操作系统(九、操作系统完整实现)
c语言·开发语言·汇编·c++·操作系统·自制操作系统