JAVA基础JVM讲解

news2024/11/15 17:42:42

JVM体系结构

在这里插入图片描述

在这里插入图片描述 

主要包括两个子系统和两个组件:

Class loader(类装载器) 子系统(用来装载.class文件);

Execution engine(执行引擎) 子系统(执行字节码,或者执行本地方法);

Runtime data area (运行时数据区域)组件(方法区、堆、java栈、PC寄存器、本地方法栈);

Native interface(本地接口)组件。

  • Class loader子系统:根据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area中的method area(方法区域)。

  • Execution engine子系统:执行classes中的指令。方法的字节码是由Java虚拟机的指令序列构成的。每一条指令包含一个单字节的操作码,后面跟随0个或多个操作数。执行引擎执行字节码时,首先取得一个操作码,如果操作码有操作数,取得它的操作数。它执行操作码和跟随的操作数规定的动作,然后再取得下一个操作码。这个执行字节码的过程在线程完成前将一直持续。任何JVM实现的核心是Execution engine, 换句话说:Sun 的JDK 和IBM的JDK好坏主要取决于他们各自实现的Execution engine的好坏。

  • Native interface组件 :与native libraries交互,是其它编程语言交互的接口。Java里声明为native的方法多数在jdk/src/<platform>/native里可以找到。其中可以是share,也就是平台中立的代码;也可以是某个具体平台。这个native目录里的结构跟Java源码结构一样是按包名来组织的。不过需要提醒的是,这些native方法不是“JVM”的,是“类库”的,不在JVM里面。

  • Runtime data area 组件:虚拟机定义了若干种程序运行时使用到的运行时数据区,有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,如堆、方法区。第二种则是与线程一一对应,随线程的开始和结束而创建和销毁,如栈,寄存器。

JVM内存模块

Java内存模型是Java语言在多线程并发情况下对于共享变量读写(实际是共享变量对应的内存操作)的规范,主要是为了解决多线程可见性、原子性的问题,解决共享变量的多线程操作冲突问题

JMM(java内存模型)Java Memory Model,本身是一个抽象的概念,不是真实存在的,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式。

JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程读/写共享变量的副本。

JMM内存模型三大特性
1、原子性
使用 synchronized 互斥锁来保证操作的原子性
2、可见性:
volatile,会强制将该变量自己和当时其他变量的状态都刷出缓存。
synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。
final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
3、有序性
源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 ->最终执行的命令。
重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
处理器在进行重排时必须考虑数据的依赖性,多线程环境线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的。

JMM定义了线程和主内存之间的抽象关系:

线程之间的共享变量存储在主内存(Main Memory)中
每个线程都有一个私有的本地内存(Local Memory),本地内存是JMM的一个抽象概念,并不真实存在,它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。本地内存中存储了该线程以读/写共享变量的拷贝副本。
从更低的层次来说,主内存就是硬件的内存,而为了获取更好的运行速度,虚拟机及硬件系统可能会让工作内存优先存储于寄存器和高速缓存中。
Java内存模型中的线程的工作内存(working memory)是cpu的寄存器和高速缓存的抽象描述。而JVM的静态内存储模型(JVM内存模型)只是一种对内存的物理划分而已,它只局限在内存,而且只局限在JVM的内存。

堆内分配:

 

JVM运行时数据区域

1 方法区 MethodArea
方法区(MethodArea)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫作“非堆”(Non-Heap),目的是与Java堆区分开来。

运行时常量池

运行时常量池(RuntimeConstantPool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(ConstantPoolTable),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

Java虚拟机对于Class文件每一部分(自然也包括常量池)的格式都有严格规定,如每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、加载和执行,但对于运行时常量池,《Java虚拟机规范》并没有做任何细节的要求,不同提供商实现的虚拟机可以按照自己的需要来实现这个内存区域,不过一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中[1]。

运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。

既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

2 Java堆Heap
对于Java应用程序来说,Java堆(JavaHeap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。在《Java虚拟机规范》中对Java堆的描述是:“所有的对象实例以及数组都应当在堆上分配”,而这里笔者写的“几乎”是指从实现角度来看,随着Java语言的发展,现在已经能看到些许迹象表明日后可能出现值类型的支持,即使只考虑现在,由于即时编译技术的进步,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生,所以说Java对象实例都分配在堆上也渐渐变得不是那么绝对了。

Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆”。从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以Java堆中经常会出现“新生代”“老年代”“永久代”“Eden空间”“FromSurvivor空间”“ToSurvivor空间”等名词,这些区域划分仅仅是一部分垃圾收集器的共同特性或者说设计风格而已,而非某个Java虚拟机具体实现的固有内存布局,更不是《Java虚拟机规范》里对Java堆的进一步细致划分。如果从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(ThreadLocalAllocationBuffer,TLAB),以提升对象分配时的效率。不过无论从什么角度,无论如何划分,都不会改变Java堆中存储内容的共性,无论是哪个区域,存储的都只能是对象的实例,将Java堆细分的目的只是为了更好地回收内存,或者更快地分配内存。

根据《Java虚拟机规范》的规定,Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的,这点就像我们用磁盘空间去存储文件一样,并不要求每个文件都连续存放。但对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的内存空间。

Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

3 虚拟机栈
每个线程运行所需要的内存空间,成为虚拟机栈;
线程私有,生命周期和线程一致;
每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存;
每个线程只能有一个活动栈帧,对应当前执行的方法。
描述的是 Java 方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。

StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。

OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存。

 

4 程序计数器
程序计数器(ProgramCounterRegister)是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要执行的指令代码,由执行引擎来读取下一条指令。更确切的说,一个线程的执行,是通过字节码解释器改变当前线程的计数器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。

保存当前执行指令的地址,一旦程序执行,程序计数器将更新到下一条指令。

为了确保线程切换后(上下文切换)能恢复到正确的执行位置,每个线程都有一个独立的程序计数器,各个线程的计数器互不影响,独立存储。也就是说程序计数器是线程私有的内存。

5  本地方法栈
本地方法栈(NativeMethodStacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务

《Java虚拟机规范》对本地方法栈中方法使用的语言、使用方式与数据结构并没有任何强制规

定,因此具体的虚拟机可以根据需要自由实现它,甚至有的Java虚拟机(譬如Hot-Spot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。

6  直接内存
直接内存(DirectMemory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。

在JDK1.4中新加入了NIO(NewInput/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据

显然,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括物理内存、SWAP分区或者分页文件)大小以及处理器寻址空间的限制,一般服务器管理员配置虚拟机参数时,会根据实际内存去设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

4 执行引擎
尽管并不是所有的Java虚拟机都采用解释器与编译器并存的运行架构,但目前主流的商用Java虚拟机,譬如HotSpot、OpenJ9等,内部都同时包含解释器与编译器。

HotSpot虚拟机中内置了两个(或三个)即时编译器,其中有两个编译器存在已久,分别被称为“客户端编译器”(ClientCompiler)和“服务端编译器”(ServerCompiler),或者简称为C1编译器和C2编译器(部分资料和JDK源码中C2也叫Opto编译器),第三个是在JDK10时才出现的、长期目标是代替C2的Graal编译器。

垃圾回收(GC)

无法手动垃圾回收,只能手动提醒,等待JVM自动回收
GC的作用区在堆(Heap)和方法区中
JVM进行GC时,并不是统一对这三区域(新生区,幸存区,老年区)统一回收,回收都是新生代
轻GC(普通GC)只针对于新生区,偶尔作用幸存区(在新生区满的情况下)
重GC(全局GC)全局释放内存
14、常见垃圾回收算法
1.引用计数算法
原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的问题。

2.复制算法
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中,同时回收未使用的对象。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理。
优点:不会出现碎片化问题
缺点:需要两倍内存空间,浪费

3.标记-清除算法
此算法执行分两阶段。第一阶段从引用根节点开始标记所用存活的对象,第二阶段遍历整个堆,把未标记的对象清除。
优点:不会浪费内存空间
缺点:此算法需要暂停整个应用,同时,会产生内存碎片

4.标记-压缩算法
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有存活的对象,第二阶段遍历整个堆,清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。
此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

分代回收策略

1.绝大多数刚刚被创建的对象会存放在Eden区
2.当Eden区第一次满的时候,会触发MinorGC(轻GC)。首先将Eden区的垃圾对象回收清除,并将存活的对象复制到S0,此时S1是空的。
3.下一次Eden区满时,再执行一次垃圾回收,此次会将Eden和S0区中所有垃圾对象清除,并将存活对象复制到S1,此时S0变为空。
4.如此反复在S0和S1之间切换几次(默认15次)之后,还存活的对象将他们转移到老年代中。
5.当老年代满了时会触发FullGC(全GC)
MinorGC
使用的算法是复制算法
年轻代堆空间紧张时会被触发
相对于全收集而言,收集间隔较短
FullGC
使用的算法一般是标记压缩算法
当老年代堆空间满了,会触发全收集操作
可以使用 System.gc()方法来显式的启动全收集
全收集非常耗时

这里写图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年6月18日DAMA-CDGA/CDGP数据治理认证报名指南

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

弹性及其应用

弹性 衡量了买者和卖者对市场环境变化作出反应的程度大小是我们能够更精确的分析供给和需求 需求的价格弹性 给定百分之一的价格变化&#xff0c;需求量变量的百分数就是需求的价格弹性它衡量了一种物品的需求量对该物品价格变化作出反应的程度大小 需求价格弹性的决定因素…

手把手教你搭建OpenStack云平台(超级详细)

一、前言 OpenStack云平台搭建需要两个节点&#xff0c;一个是controller&#xff08;控制节点&#xff09;&#xff0c;另一个是compute&#xff08;计算节点&#xff09;。 控制节点&#xff08;controller&#xff09;规划如下&#xff1a; 一块200G的硬盘。两块网卡&#x…

天地图下载数据的方法

天地图下载数据的方法&#xff0c;目前我知道的&#xff0c;部分数据可以下载&#xff0c;部分数据不提供下载&#xff08;但是有数据&#xff09;具体就不知道原因了&#xff0c;话不多说&#xff1a; 登录全国地理信息资源目录服务系统&#xff08;全国地理信息资源目录服务…

cuda编程

https://docs.nvidia.com/cuda/index.html 这里写目录标题 OpenCL 与OpenGlrelease noteCUDA Features Archivensight visual studio安装指导本地下载和网络下载 bili 视频1c2050 版本Fermi架构3接口延迟cpu逻辑控制&#xff0c;管理 GPU计算核心连接 GPU硬件架构综述架构2atom…

Revit中如何使创建的族文件内存变小

族文件的大小直接影响到项目文件的大小和软件运行速度&#xff0c;如何将族文件做的最小并且满足项目需求呢? 方法一&#xff1a;清除未使用项 1. 族制作完成可以把族文件中未用到的外部载入族或其他多余数据删掉&#xff0c;点击“管理”选项卡下拉的“清除未使用项”命令; 2…

我拒绝了字节的offer,给求职者提个醒....

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司&#xff0c;一干就是2年。我想说的是&#xff0c;但凡有点机会&#xff0c;千万别去外包&#xff01; 在深思熟虑过后&am…

LabVIEWCompactRIO 开发指南第七章45 将I/O添加到Compact RIO

LabVIEWCompactRIO 开发指南第七章45 将I/O添加到Compact RIO 基于LabVIEW可重配置I/O&#xff08;RIO&#xff09;架构的NI产品日益被采用用于需要高通道数、密集处理和分布式I/O的系统级应用。将RIO扩展I/O添加到NI RIO产品产品中&#xff0c;可实现1&#xff1a;N系统拓扑…

源码分析 | MySQL 的 commit 是怎么 commit 的?

作者&#xff1a;李鹏博 爱可生 DBA 团队成员&#xff0c;主要负责 MySQL 故障处理和 SQL 审核优化。对技术执着&#xff0c;为客户负责。 本文来源&#xff1a;原创投稿 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。 …

JavaEE进阶(Mybatis)5/30

目录 1.单元测试 2.生成单元测试 3.Mybatis 插入操作 4.#{}和${}的区别 1.单元测试 优点&#xff0c;1.可以检查方法的正确性 2.可以&#xff0c;在打包前检查程序的错误&#xff0c;减少错误 3.不污染数据库 2.生成单元测试 1.要生成的类内部generate 2.添加单元测试的代码…

5.31串讲Spring、Vue相关问题

5.31串讲 SSM相关问题 文章目录 5.31串讲 SSM相关问题Spring Security&#xff08;Shiro&#xff09;Security框架认证流程Security流程图展示 Vue相关指令四个阶段 axios Spring Security&#xff08;Shiro&#xff09; Spring Security是一个基于Spring 的安全框架&#xff…

高压放大器怎么驱动超声波探头的

高压放大器是超声波探头中的关键部分&#xff0c;它主要用于放大发射信号和接收信号。在超声波成像过程中&#xff0c;发射信号需要经过高压放大器的放大后才能刺激探头产生超声波&#xff0c;并且接受到的超声波也需要通过高压放大器进行放大处理。因此&#xff0c;高压放大器…

ChatGPT正式登陆iOS平台

6天前&#xff0c;ChatGPT在美区App Store中上架了官方App&#xff0c;累计下载量已经突破 50 万次&#xff0c;OpenAI 的 ChatGPT 应用在上架之后&#xff0c;其热度远超必应聊天等聊天机器人&#xff0c;以及其它使用 GPT-4 的第三方应用。 3.5是免费的&#xff0c;GPT4是收…

【大数据之Hive】五、Hiveserver2服务部署

hiveserver2服务 hiveserver2提供JDBC/ODBC接口&#xff0c;使得用户可以远程访问Hive数据&#xff0c;即作为客户端的代理与Hadoop集群进行交互。 hiveserver2部署时需要部署到一个能访问集群的节点上&#xff0c;保证能够直接往Hadoop上提交数据。   用户在客户端提交SQL语…

8个免费UI图标网站,任何设计师都不该错过

图标是网页设计和应用程序界面设计中不可缺少的一部分。但在日常工作中&#xff0c;许多设计师每次都需要设计一套特定的图标&#xff0c;但他们不知道在哪里获得高质量的UI图标素材。 本文推荐8个宝藏级UI图标完整资源网站&#xff0c;免费&#xff0c;建议迅速收集以备不时之…

Linux网站搭建(nginx+php+mysql)

中间件 web服务器&#xff1a;nginx&#xff08;前端网页&#xff09;php 数据库服务器&#xff1a;mysql 思路&#xff1a; 先安装中间件nginx&#xff0c;nginx要采用源码安装。安装按之后启动&#xff0c;可以访问到一个静态初始页面&#xff1b;然后安装php&#xff08;超…

可节省40%MCU开发成本的音乐睡眠灯语音扩展芯片方案N9300-S16

随着社会节奏的加快&#xff0c;人们每天的生活节奏也在不断的加快&#xff0c;工作压力也在不断的加大&#xff0c;越来越多的人都面临着失眠的痛苦&#xff0c;当拖着疲惫不堪的身体躺到床上时&#xff0c;却发现由于担忧每天的工作或月底需要交房租等问题&#xff0c;久久无…

『赠书活动 | 第七期』《硅基物语》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第七期』 本期书籍&#xff1a;《硅基物语》 赠书规则&#xff1a;评论区&#xff1a;点赞&#xff5c;收藏&#xff5c;留言 评论区留言&a…

数据为基 成峰化海|GBASE南大通用openGauss Developer Day 2023精彩看点一览

5月26日&#xff0c;面向数据库开发者的年度技术盛会「openGauss Developer Day 2023」在北京圆满召开。本届大会以“聚数成峰&#xff0c;共赢未来”为主题&#xff0c;汇集产学研用各层面技术专家、企业领军&#xff0c;分享openGauss社区前沿技术创新、优秀实践和生态成果。…

linux内核升级详细步骤

# 安装yum源; rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm # 查看内核可用版本 yum --disablerepo"*" --enablerepo"elrepo-kernel" list available # 安装最…