JVM快速入门(1)JVM体系结构、运行时数据区、类加载器、线程共享和独享、分区、Java对象实例化

5.1 JVM体系结构

在这里插入图片描述

  • 线程独占区-程序计数器(Program Counter Register)

    • 程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器;
    • 在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成;
    • 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
  • 线程独占区-Java虚拟机栈(Java Virtual machine Stacks)

    • Java虚拟机栈与线程生命周期相同。其描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。

    • 局部变量表存放了编译器可知的各种基本数据类型(boolen,byte,char,short,int,float,long,double),对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)。

    • 在这个区域中,Java虚拟机规范规定了两种异常情况:

    • 如果线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverFlowError异常;
    • 如果虚拟机栈可以动态扩展(当前大部分Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),并且扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
  • 线程独占区-本地方法栈(Native Method Stack)

    • 本地方法栈与虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,本地方法栈为虚拟机使用到的Native方法服务。
    • 由于虚拟机规范中没有对本地方法栈中的语言、使用方式与数据结构进行强制规定,有的虚拟机(入Sun HotSpot虚拟机)直接把本地方法栈和虚拟机栈合二为一。
    • 本地方法栈会抛出StackOverflowError异常和OutOfMemoryError异常。
  • 线程共享区-Java堆(Java Heap)

    • Java堆是Java虚拟机所管理的内存中最大的一块,在虚拟机启动时创建,其唯一目的就是存放对象实例:所有的对象实例以及数组都要在堆上分配(但随着JIT编译器的发展与逃逸分析技术逐渐成熟,所有的对象分配在堆上也渐渐不是那么绝对了)。
    • Java堆是垃圾收集器管理的主要区域,现在收集器基本采用分代收集算法,所以Java堆还可细分为:新生代和老年代;再细致点分为Eden空间(伊甸园区),From Survivor空间(幸存区0),To Survivor空间(幸存区1)等;
    • 根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可;
    • 如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
  • 线程共享区-方法区(Method Area)

    • 方法区用于存储已被虚拟机加载的类信息(构造方法、接口定义)、常量、静态变量、即时编译器编译后的代码(运行时常量池)等数据
    • 所谓的方法区为永久代的说法,仅仅是因为HotSpot虚拟机将GC分代收集扩展至方法区,或者说使用永久代来实现方法而已。对于其他虚拟机是不存在永久代的说法的。
    • 运行时常量池是方法区的一部分,用于存放编译器生成的各种字面量和符号引用,一般还会存放翻译出来的直接引用。这部分内容将在类加载后进入方法区的运行时常量池中存放。当常量池无法再申请到内存时抛出OutOfMemoryError异常。
  • 举例:

            String str1 = "abd";
            String str2 = new String("abd");
    
            System.out.println(str1 == str2);
            System.out.println(str1 == str2.intern());
    
    输出:
        false
        true
    
    分析:
       (1) String str1 = "abc" ,str1指向常量池;
       (2) String str2 = new String("abc");str2指向堆内存对象,二者地址不同所以str1 == str2 结果为false;
       (3) 但是str2.intern()会把字符串值从堆内存移动到常量池中(如果常量池存在则返回该值的地址),这样一来str2和str1都是指向常量池的abc。  
        如下图所示:     
    

    在这里插入图片描述

5.2 JVM详细架构图

在这里插入图片描述

5.3 JVM架构之运行时数据区

  • 线程共享区包括:堆、元空间
  • 线程私有区包括:虚拟机栈、本地方法栈、程序计数器

运行时数据区

包括:程序计数器(PC寄存器)、Java虚拟机栈、Java堆、方法区、运行时常量池、本地方法栈等等。

5.3.1 PC 寄存器,也叫程序计数器
  • 1、JVM支持多个线程同时运行,每个线程拥有一个程序计数器,是线程私有的,用来存储指向下一条指令的地址。
  • 2、在创建线程的时候,创建相应的程序计数器。
  • 3、执行本地native方法时,程序计数器的值为undefined。
  • 4、是一块比较小的内存空间,是唯一一个在JVM规范中没有规定OutOfMemoryError的内存区域。
5.3.2 虚拟机栈
  • 栈是由一系列帧(Frame)组成(因此Java栈也叫作帧栈),是线程私有的。
  • 帧是用来保存一个方法的局部变量、操作数栈(java没有寄存器,所有的参数传递使用操作数栈)、常量池指针、动态链接、方法返回值等。
  • 每一次方法调用创建一个帧并压栈,退出方法的时候,修改栈顶指针就可以把栈帧中的内容销毁。
  • 局部变量表存放了编译期可知的各种基本数据类型和引用数据类型、每个slot存放32位的数据,long、double占两个槽位。
  • 栈的优点:存取速度比堆快,仅次于程序计数器。
  • 栈的缺点:存在栈中的数据太小,生存期是在编译期决定的,缺乏灵活性。
  • StackOverflowError异常:当线程请求的栈深度大于虚拟机所允许的深度;
  • OutOfMemoryError异常:如果栈的扩展时无法申请到足够的内存。
5.3.3 Java堆
  • 用来存放应用系统创建的对象和数组,所有线程共享Java堆。
  • GC主要管理堆空间,对分代GC来说,堆也是分代的。
  • 堆的优点:运行期动态分配内存大小,自动进行垃圾回收。
  • 堆的缺点:效率相对较慢。
5.3.4 方法区的理解
  • 对于HotSpotJVM而言,方法区还有一个别名,叫做Non-Heap(非堆),目的就是和堆区分开,所以方法区看做是一块独立于Java堆的内存空间。
  • 方法区(Method Area)和Java堆一样,是各个线程共享的内存区域;
  • 方法区在JVM启动时被创建,并且它的实际的物理内存空间和Java堆区一样,都是可以不连续的;
  • 方法区的大小,和堆空间一样,可以选择固定大小或者可扩展;
  • 方法区的大小,决定了系统可以保持多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError:Metaspace;
  • 关闭JVM就是释放这个区域的内存。

HotSpot中方法区的演进:

  • JDK1.6及之前,有永久代,静态变量存放在永久代上,常量池在方法区了;
  • JDK1.7,有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中;
  • JDK1.8及之后,无永久代,类型信息、字段、方法、常量保存在本地内存的元空间。但字符串常量池、静态变量仍在堆上。
  • 永久代、元空间二者并不只是名字改变了,内部结构也调整了。
5.3.5 运行时常量池:
  • 运行时常量池是方法区的一部分;

  • 常量池是Class文件的一部分,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中;

  • 在加载类和接口到JVM中,就会创建对应的运行时常量池;

  • JVM为每个已加载的类型(类或接口)都维护一个常量池,池中的数据项像数组一样,通过索引访问的;

  • 运行时常量池中包含各种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能获得的方法或字段引用。此时不再是常量池中的符合地址了,这里换为真实地址:

  • 运行时常量池,相对于Class文件常量池的另一重要特征是:具备动态性;
  • String.intern()
  • 党创建类或接口的运行时常量池时,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则JVM就会抛出OOM异常。
5.3.6 本地方法栈
  • 在JVM中用来支持native方法执行的栈就是本地方法栈。
  • 在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

在这里插入图片描述

5.4 类加载器

  • 作用:加载class文件

  • 加载器:

    • 1.虚拟机自带的加载器
    • 2.启动类(根)加载器(Bootstrap classLoader):主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和AppClassLoader。
    • 3.扩展类加载器(ExtClassLoader):主要负责加载jre/lib/ext目录下的一些扩展的jat.
    • 4.应用程序加载器(AppClassLoader):主要负责加载应用程序的主函数类。
  • 图示:

    在这里插入图片描述

  • 双亲委派机制

    • (1)类加载器收到类加载的请求;

    • (2)将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器(根加载器);

    • (3)启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器;否则,抛出异常,通知子加载器进行加载;

    • (4)重复步骤3

    ​ Class Not Found

    null: java 调用不到 c/c++写的调用不到

5.5 Java对象的实例化过程

java世界里面对象无处不在,在创建对象的时候主要经过哪些步骤?

5.5.1 对象的创建过程

类加载检查–>分配内存–>初始化零值–>设置对象头–>执行init方法

如图:

在这里插入图片描述

1.类加载检查
   虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用并且检查这个符号引用代表的类是否被加载,解析,初始化过,如果没有,那必须执行相应的类加载过程

new 的指令对应到语言层面上讲: new关键词,对象克隆,对象序列化等

2.分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存,对象所需内存的大小在类加载完成后便可完成确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来
3.初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值()不包括对象头

4.设置对象头(分不同的操作系统,如32位的,64位的)

初始化零值之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等这些信息,这些信息存放在对象的对象头Object Header之中 在HotSpot虚拟机中,对象在内存中的储存布局可以分为3块区域: 对象头(Header),实例数据(Instance Data)和对齐填充(Padding)

HotSpot虚拟机的对象头包括两部分信息:

     第一部分: 储存对象本的运行时数据,如哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等

    第二部分: 类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

如下是32 位的对象头

在这里插入图片描述

5.执行init()方法

对象按照程序员的意愿进行初始化;对应到语言层面来讲,就是属性赋值(注意:这是程序员自己赋的值)和执行构造方法

5.5.2 从Java源码到编译class到加载整体过程

对象创建的过程,主要经过如下5步:

  1. 判断类有没有被加载

  2. 如果没有被加载过(才开始加载类(就是类的加载过程))

  3. 初始化 :就是给一些变量进行初始化。

  4. 设置对象头(比较难理解)。

  5. 执行方法: 对对象进行赋值,和执行构造方法。

这里再从源码.java文件到编译的.class文件到加载,详细描述第2步中的类的加载过程:

加载.class文件的时候 window系统下调用底层的应该jvm.dll文件创建java虚拟机去创建一个引导类加载器(C++实现的) 此时java虚拟机已经创建 此时会调用java实现的类加载器启动 加载loadClass方法加载真正的磁盘文件上面的字节码文件,再去发起调用main()方法,此时程序就启动了

类加载到使用整个过程有如下几步: 加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载

1、加载:

​ 在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用 类的main()方法,new对象等等

2、验证:

​ 校验字节码文件的正确性

3、准备:

​ 给类的静态变量分配内存,并赋予默认值 比如Boolean类型默认 false 这些默认值是java虚拟机自己规定的,如果是加final修饰直接就会变成常量 直接赋值了

4、解析:

​ 将符号引用替换为直接引用,就是会将该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链 接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接 引用,下节课会讲到动态链接(在类加载的时候可能不会加载 只有程序运行到这里才会去加载)

5、初始化:

对类的静态变量初始化为指定的值,执行静态代码块

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/483203.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++:练习题

一、构造、析构顺序 C c; int main() {A a;B b;static D d;return 0; } //构造顺序:C A B D //析构顺序:~B ~A ~D ~C 二、拷贝构造次数 以下代码共调用多少次拷贝构造? Widget f(Widget u) //第一次:传值拷贝构造 {Widget v(u…

【QT+QGIS跨平台编译】之九十:【QGIS_Crashhandler+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、QGIS_Crashhandler介绍二、QGIS下载三、文件分析四、pro文件五、编译实践一、QGIS_Crashhandler介绍 QGIS_Crashhandler模块是QGIS中的一个重要组成部分,它提供了QGIS程序的错误崩溃处理与跟踪。 二、QGIS下载 QGIS网址: QGIS Source Download 获取最新版本的…

【Linux系统编程(进程编程)】进程的退出:父进程等待子进程的退出之僵尸进程与孤儿进程

文章目录 一、进程退出1.1、进程正常退出方式1.2、异常退出 二、父进程等待子进程退出(一)2.1、为什么要等待子进程退出2.2、(1)父进程等待子进程退出并收集子进程的退出状态如何等待wstatus空wstatus非空 2.3、(2&…

数据背后的力量:揭秘中间件中的二分查找与树结构应用

平时写业务代码的时候很少写对应的算法,因为很少会在内存中存储大量数据,在需要比较大量数据的查找时,多会依赖的中间件,而中间件底层就应用了很多不同算法,尤其是树结构的查找存储算法,二分查找算法在树里…

状态管理@Prop、@Link装饰器

Prop Link 父子组件进行数据同步化 prop 单向同步 只支持string、number、boolean、enum类型 负组件对象类型,总组件是对象类型 不可以是数组、any 不允许子组件初始化 Link双向同步 父子类型一直:string、number、boolean、enum、object、class以及他们…

centos7 yum 安装mysql8.0.36

一、前置条件:CentOS Mini 安装需要先安装ifconfig 和 wget工具,参考步骤: 1.首先,让我们找出哪个包提供了ifconfig命令。要完成这项任务,输入以下命令: yum provides ifconfig 输出: [rootlo…

Vue3 + Django 前后端分离项目实现密码认证登录

1、功能需求 通常中小型前后端项目,对安全要求不高,也可以采用密码认证方案。如果只用django来实现非常简单。采用 Vue3 前后端分离架构,实现起来稍繁琐一点,好处是可以利用各种前端技术栈,如element-plus UI库来渲染…

Android Jetpack Compose基础之组件的帧渲染

Android Jetpack Compose基础之组件的帧渲染 组合布局LayoutModifier示例 LayoutCompsable示例 绘制CanvasDrawModifierDrawModifier-drawWithContent示例 DrawModifier-drawBehind源码示例 DrawModifier-drawWithCache源码示例 拓展Modifier.graphicsLayer Android View 系统&…

6-191 拓扑排序

一项工程由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其他子任务后才能执行。例如,下图表示了一项工程若干子任务之间的先后关系。 编写函数输出所有子任务的拓扑序列。 函数接口定义: Status Push_SeqStack(SeqStack &s, ElemType x)//入栈,x入到…

Polar 2024春季个人挑战赛 Jay17 WP

Polar 2024春季个人挑战赛 Rank&#xff1a;7 【WEB】机器人 开题 起手敏感文件robots.txt 【WEB】PHP反序列化初试 最简单的php反序列化 POC&#xff1a; <?php class Easy{public $name;public function __wakeup(){echo $this->name;} } class Evil{public $evi…

2、Jenkins持续集成-gitlab安装和源码上传

文章目录 1、Gitlab代码托管服务器安装2、源代码上传托管 环境&资源准备 统一采用VMware中安装CentOS7&#xff0c;安装教程&#xff0c;统一设置静态IP资源包都存在于我的资源里面 资源版本&位置 名称机器IP软件代码托管服务器192.168.2.100Gitlab-12.4.2持续集成服…

idm下载器 idm网络加速器 Internet Download Manager(IDM)免激活不弹窗版

idm下载器官方版英文名为Internet Download Manager。是一款界面简单、体积小巧下载速度飞快的下载工具&#xff0c;支持断点续传、多个任务同时下载&#xff0c;同时还支持限速下载&#xff0c;在自义文件类型里还可以自由设置下载的文件类型。 一、IDM软件安装 Internet Dow…

Linux安装Nginx及配置TCP负载均衡

目录 1、安装编译工具及库文件2、下载解压Nginx压缩包3、Ngnix配置Tcp负载均衡4、配置Ngnix的文件5、Nginx启动 1、安装编译工具及库文件 yum -y install make zlib zlib-devel gcc-c libtool openssl openssl-devel pcre-devel2、下载解压Nginx压缩包 wget https://nginx.o…

Docker 安装 Tomcat

目录 1. 总体步骤 2.安装tomcat 2.1 搜索 2.2 拉取 2.3 查看是否拉取到镜像 2.4 运行镜像 2.5 访问tomcat首页 1. 总体步骤 搜索镜像拉取镜像查看镜像启动镜像停止容器移除容器 2.安装tomcat 2.1 搜索 2.2 拉取 docker pull tomcat 2.3 查看是否拉取到镜像 docker …

连接医患的桥梁:深入了解互联网医院APP的开发与优化

当下&#xff0c;互联网技术的不断发展&#xff0c;越来越多的医院和医疗机构开始关注并投入到互联网医院APP的开发与优化中。接下来&#xff0c;小编将与大家共同探讨互联网医院APP的开发与优化。 一、互联网医院APP的开发原则 &#xff08;1&#xff09;用户体验至上 界面设…

卷积篇 | YOLOv8改进之主干网络中引入可变形卷积DConv

前言:Hello大家好,我是小哥谈。可变形卷积模块是一种改进的卷积操作,它可以更好地适应物体的形状和尺寸,提高模型的鲁棒性。可变形卷积模块的实现方式是在标准卷积操作中增加一个偏移量offset,使卷积核能够在训练过程中扩展到更大的范围,从而实现对尺度、长宽比和旋转等各…

git基础-查看提交历史

查看提交历史 在创建了多个提交之后&#xff0c;或者如果克隆了一个具有现有提交历史的存储库&#xff0c;可能会想要回顾一下发生了什么。最基本和强大的工具就是 git log 命令。 运行下git log查看下输出状态 默认情况下&#xff0c;不带任何参数运行 git log 命令会以逆时…

拌合楼管理系统(八) c#海康威视摄像头车牌识别

前言: c#调用海康威视SDK实现车牌识别 原本以为海康威视sdk的Demo里面没有车牌识别的实例,后来发现自己肤浅了,官方是有提供的,只是车牌识别是通过安防布警的方式实现的.程序主动监听,触发告警后获取到车牌信息. 一、接口调用的流程&#xff1a; 首先初始化sdk -> 开…

袁志佳:前端全栈工程师精英班

教程介绍 本套课程涵盖大前端的全部领域&#xff0c;并对传统的Web前端全栈深入教学。如利用前端知识开发 AI、VR、AR、iOS、Android、PC、Server、智能硬件。 同时我们将核心打造 JavaScript语言新发展、Vue源码分析、前端持续集成方案汇总、MV*框架深度分析 、前端图形学、N…

亚稳态及其解决办法

异步电路 亚稳态 亚稳态亚稳态的产生原因什么是同步异步信号怎么消除亚稳态 亚稳态 在数字电路中&#xff0c;每一位数据不是1&#xff08;高电平&#xff09;就是0&#xff08;低电平&#xff09;。当然对于具体的电路来说&#xff0c;并非1&#xff08;高电平&#xff09;就是…