【Java EE】-JVM

news2024/9/22 17:31:19

作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏:【JavaEE】
分享:
雨下整夜
我的爱溢出就像雨水
——《七里香》

主要内容:JDK,JRE,JVM三者之间的联系。JVM内存区域划分:本地方法栈(Native Method Stacks),虚拟机栈(JVM Stacks),程序计数器(PC),堆区(Heap),元数据区(MetaSpace)。JVM类加载机制,JVM类加载时机,JVM类加载过程,JVM类加载方式(双亲委派模型)。JVM垃圾回收机制(GC),如何判定一个对象是垃圾?看是否有引用指向这个对象。如何清理垃圾?标记清除,复制算法,标记整理,分代回收算法。

在这里插入图片描述

文章目录

    • 一、JDK JRE JVM
    • 二、JVM 内存区域划分
    • 三、JVM 类加载机制
      • 1)类加载时机
      • 2)类加载的过程
      • 3)双亲委派模型(类加载的方式)
    • 四、JVM 垃圾回收机制
      • 1)什么是 GC
      • 2)GC 如何判定对象是否是垃圾
        • 2.1> 引用计数
        • 2.2> 可达性分析
      • 3)GC 如何清理垃圾
        • 3.1> 三个基本回收算法
          • a 标记清除
          • b 复制算法
          • c 标记整理
        • 3.2> 分代回收算法
        • 3.3> 垃圾回收器

一、JDK JRE JVM

问题·:我们都知道,要想在电脑上运行Java程序,首先得安装上jdk,那么jdk是啥?它是拿来干啥的?为啥安装好jdk后就可以运行Java程序了?

JDK: 全称 Java Development Kit(Java开发工具包),包含Java运行时环境(JRE),Java开发工具。Java开发工具又包含Java编译器(Javac),Java学习文档和代码示例等。
Javac负责将Java源代码编译为字节码文件(.class文件)。

JRE: 全称 Java Runtime Environment(Java运行时环境),包含Java虚拟机和Java类库(Java标准库和API)。

JVM:全称Java Virtual Machine(Java虚拟机):
JVM 负责将Java字节码翻译成计算机可执行的机器码,并提供了一系列的运行时环境支持。
Java虚拟机中包含:
类加载器(Class Loader) :负责将编译后的字节码加载到内存中。
执行引擎(Execution Engine):负责执行加载到内存中的字节码。
运行时数据区(Runtime Data Area):包含了Java程序运行时所需要的内存空间,如方法区、堆、栈等。
垃圾回收器(Garbage Collector):负责自动回收不再使用的对象所占用的内存空间。


总的来说:JDK和JRE以及JVM的关系如下,(如下图所示):
JDK = JRE + Java开发工具
JRE = JVM + Java核心类库

在这里插入图片描述

解答:了解了上述的JDK、JRE、JVM后,我们不难理解,当我们安装好JDK后,就可以使用JDK中的Java编译器 将Java源代码编译成字节码文件,然后使用JRE中的 JVM来执行这些 字节码文件。JVM会将字节码翻译成计算机可执行的机器码,并提供运行时环境支持,使得Java程序能够在计算机上运行。

二、JVM 内存区域划分

在这里插入图片描述

可分为五个部分:
① 本地方法栈(Native Method Stacks):
用于记录native方法调用。是给要调用的 native方法(是由C/C++实现的JVM内部的方法)准备的栈空间。
② 程序计数器(Program Counter Register):
简称PC,PC存储着当前线程即将执行的指令的地址。
③ 虚拟机栈(JVM Stacks):
用于记录方法调用。每个线程有一份虚拟机栈,当该线程中有一个方法调用时,会在虚拟机栈中申请一个栈帧(开辟空间空间),并记录下当前方法的调用关系。每个栈帧会包含入口地址,参数值、局部变量、返回地址等等。
注意:每个线程中调用方法时,会为方法申请函数栈帧,这个函数栈帧是在虚拟机栈中的,一个线程对应一个虚拟机栈,而虚拟机栈中的一个栈帧对应一个方法调用。

④ 堆区(Heap):
new出来的对象,都是在堆上的。因此堆的空间比其他空间大很多。一个进程对应一个堆,该进程中的线程共享该堆空间。堆的空间会使用垃圾回收机制。
⑤ 元数据区(Metaspace),或方法区:
用于存储类对象、常量池、静态成员。JVM虚拟机加载解析好.class文件后,会将类对象保存到这里。
注意:每个Java程序在运行时都会创建一个Java虚拟机(JVM)实例,一个进程对应一个Java虚拟机,同时一个进程有一个堆和一个元数据区。

三、JVM 类加载机制

1)类加载时机

在Java程序启动时,首先使用javac编译将源文件(.java)编译生成字节码文件(.class),然后由Java虚拟机(JVM) 解析和执行字节码文件,最开始是将包含“main”方法的主类加载并解析;程序运行过程中,后续需要使用其他类的时候,再进行所需类的类加载操作。

类加载是在Java程序运行过程中第一次使用类时发生的。包含下面几种触发情况:
① 创建类的实例对象时,会先进行类加载。比如 new 一个对象时,会先加载这个对象所属的类。
② 访问类的 静态成员(静态变量,静态方法)时,触发类加载。
③ 使用反射机制访问类时,会先类加载。如获取类的class对象,调用类的方法等,都会先进行类加载。
④ 当启动Java程序时,会先加载包含"main"方法的主类。

2)类加载的过程

在这里插入图片描述

在Java中,类加载的过程由三个阶段组成:加载(Loading)、连接(Linking)、初始化(Initialization)。其中,连接阶段又包括验证(Verification)、准备(Preparation)、解析(Resolution) 三个步骤。下面我们就来了解这些阶段分别做了什么。

Loading:加载阶段,通过类的名字来查找编译后生成的对应类的字节码文件,并将字节码文件读取到内存中。
Linking:连接阶段包含三个部分。

Verification:验证字节码文件的正确性和安全性,确保符合Java虚拟机规范。
Preparation:准备,为类对象分配空间,并将内存空间赋予默认值"0",保存部分类信息。
Resolution:解析,对常量池进行初始化,虚拟机将常量池中类或接口等的符号引用转换成直接引用,以便定位到实际的内存空间。(在解析阶段,虚拟机会根据符号引用的信息,通过在方法区中查找类信息(在Preparation阶段保存的),来找到对应的直接引用)

注解:符号引用是一种以符号形式进行引用的方式,它包括类的名称、方法描述等,但是不具体指向实际空间上的某个位置。而直接引用则是内存中直接指向目标空间的指针、偏移量等。

Initialization:初始化,对类的对象进行真正的初始化操作,比如执行构造方法,执行静态代码块代码等。

3)双亲委派模型(类加载的方式)

1> 什么是双亲委派模型?
在Java中,所有的类都需要 类加载器(ClassLoader) 进行加载才能被JVM识别和使用。
双亲委派模型就是类加载器的一种工作机制:当一个类加载器加载一个类时,它会首先将加载请求交给父类加载器去完成,直到父类加载器无法加载时,才由子类加载器加载。需要注意的是:这里的父类加载器和子类加载器之间不是继承关系,而是理解为层次关系。

在这里插入图片描述

2> 双亲委派模型包含哪些来加载器?
如上图就是一个双亲委派模型(用户自定义添加了类加载器)
·BootstrapClassLoader·是启动类加载器,负责加载标准库中的类。ExtensionClassLoader是扩展加载类,负责加载JVM开发厂商提供的扩展库中的类。
ApplicationClassLoader是应用库扩展类,负责加载用户代码中的第三方库和项目中的类。
下面两个是用户自定义类加载器,可有可无。根据用户自己的需求而定。

3> 例子理解

举个例子,比如上图中,此处需要加载一个类时,会先从自定义加载器开始,但是自定义加载器不会直接进行这个类的加载,而是将加载请求交给它的父类加载器ApplicationClassLoader。但是AppClassLoader也不会直接进行这个类的加载,而是将加载请求交给它的父类加载器ExtensionClassLoader。ExtensionClassLoader也不会直接进行加载,而是将加载请求交给其父类加载器BootstrapClassLoader。BootstrapClassLoader会想要将加载请求交给它的父类加载器,但是它的父类加载器为null了,因此BootstrapClassLoader就根据要加载的这个类信息查找负责自己加载的标准库的相关类,如果找到就加载,如果找不到就算了。接着交给ExtensionClassLoader,也是查找自己负责加载的扩展库中的相关类,找到就加载,找不到就算了。然后交给ApplicationClassLoader,进行相应操作,如果还有自定义加载器也是一样。
总的来说:和是类似递归的方式实现。

4> 双亲委派模型的优点?
优点:它可以保证Java类的安全性和一致性。 比如你自定义实现了一个Integer类时,类加载的双亲委派模型方式会先使用父类加载器进行加载,因此会先加载到标准库中的Integer,这样可以防止和冲突。


注意:双亲委派模型也不一定要必须遵守。具体情况看需求。

四、JVM 垃圾回收机制

1)什么是 GC

1> GC,全称Garbage Collection,垃圾回收机制。

理解GC
我们知道,在C/C++中可以动态申请内存空间(malloc),这一部分的空间是在堆上的,当我们的程序不再需要使用这部分空间时,需要手动将动态申请的这一部分空间释放(free)掉,防止发生内存泄漏。
GC就是要做把不用的空间释放,归还给操作系统这样的操作。即GC就是Java虚拟机中的垃圾回收器自动帮我们回收了垃圾,而不需要我们手动回收垃圾,释放空间。像Java,python,Go,PHP,JS都是使用GC来解决垃圾回收的问题的。

问题1:我们知道,当进程关闭的时候(也就是退出程序),代码中开辟的空间就自动释放了。那么为啥必须将内存释放,似乎不释放也行?
解释:当程序作为客户端的程序的时候,确实是进程关闭,空间释放,即使是内存泄露一些也没多大影响,我把程序关了就行。但是,如果在服务器端就出大问题了,因为服务器7*24小时运行,很小的内存泄漏经过日积月累就很有可能成为一颗定时炸弹,不知道哪一天爆发。因此,应当时刻考虑内存泄露的问题。
问题2那么C/C++为啥不使用GC呢?
解释:原因是GC会消耗一定的资源,并且在垃圾达到一定量的时候进行清理时可能会出现卡顿问题,这个问题又称stw(Stop The World),但是C/C++追求的是极致的性能,因此没有采用GC来进行垃圾回收,而是需要程序员进行手动垃圾回收。

2> GC 的操作单位是对象,而不是字节

①垃圾回收(GC) 主要是针对堆区进行的。因为堆区是Java虚拟机分配对象内存的主要区域。
②垃圾回收(GC) 是以"对象"作为基本单位进行垃圾回收,即当这个对象的成员变量,方法等各种属性后续代码中不再用到时,就会进行垃圾回收。

3> GC 的实际工作流程

①找到垃圾,判定垃圾
②清理垃圾,释放空间

2)GC 如何判定对象是否是垃圾

问题:那么,怎么判断这个对象是否是垃圾呢?
核心思路:看是否有引用指向这个对象。

具体实现:
①引用计数
②可达性分析

2.1> 引用计数

在Python,PHP中的 垃圾回收(GC)使用的就是引用计数的方法。即给每个对象配备一个计数器,当有引用指向这个对象时,计数器+1,当引用被销毁时,引用-1。

引用计数进行垃圾判断的优缺点?
优点在于简单方便。
缺点在于:

有时内存浪费很多。当对象的空间很小且数量很多时,比如大量整形的对象(new Integer()),如果使用给这个对象配备一个计数器,那么就相当于增大了一倍的空间,浪费空间就很大。
存在循环引用的问题
举个例子,如下图:在指向下面的前两部分的代码后,1号对象和2号对象的引用计数都是2。当把car1和car2置为空,即不再指向它原来的对象时,可以发现1号对象和2号对象的引用都还是为1,这两个对象无法回收,本质就是car1的成员变量car还指向2号对象,car2的成员变量car还指向1号对象,但是car1和car2的car变量无法再被访问,因为car1和car2引用无了。
因此如果使用引用计数的方法来确定垃圾,还需要解决这个问题,在python和PHP中是搭配了其他机制,来解决循环引用的问题。
在这里插入图片描述

2.2> 可达性分析

①怎么进行可达性分析?
把new出来的对象,整体上串起来,类似于一个有向图。可达性分析时,使用根搜索算法,这个算法以一组"根"的起始对象开始,从这些根对象出发,递归遍历(DFS或BFS)所有的引用关系,标记所有可达的对象。再和所有对象的名单比对,不可达的对象就可以回收,释放空间。
我们再来看看引用计数的循环引用的问题在这里怎么样:不难发现,引用car1和car2的car已经无法找到,自然car1的car对2号对象的引用也就无法访问,进而就是2号对象不可达,car2的car对1号对象的引用无法访问,进而就是1号对象不可达,所以最后1号和2号对象都可以作为垃圾清除。

②根对象的选取?
虚拟机栈的栈帧中的局部变量引用的对象
方法区中的静态成员变量

3)GC 如何清理垃圾

基本方法有:标记清除、复制算法、标记整理三个算法。分代回收算法则是"复合型"算法,除此之外,还有并发标记算法CMS,还有G1,ZGC算法等。

3.1> 三个基本回收算法

a 标记清除

在可达性分析时,给所有可达的对象打上存活标记。在清除阶段,垃圾回收器遍历整个堆内存,清除未被标记的对象,相对而言就是清除标记为垃圾的对象。

标记清除的优点在于:简单方便。
缺点:内存碎片化。 如果后续需要在堆区申请一块大的空间,有可能因为内存碎片化的问题导致无法成功。

在这里插入图片描述

b 复制算法

将整个堆空间分成两半,首先使用左边一半(或右边一半也行),进行可达性分析后,就把可达的对象标记为存活对象,未标记的对象即相对的标记为垃圾的对象。当左边的空间达到一定的阈值时,会触发复制操作。将标记为存活的对象复制到右边,再把左边的整个空间全部释放。右边快满时,也做相应的操作。
复制算法优缺点:使用复制算法可以避免内存碎片化的问题。但是如果进行复制操作时,垃圾很少,但是存活的对象太多,这样复制的成本就大了。

在这里插入图片描述

c 标记整理

进行可达性分析后,就将可达的对象标记为存活,未标记的对象即为垃圾。整理时,从前到后将标记为存活的对象在这个空间里从前到后排列(可能会覆盖标记为垃圾的对象),然后把最后一个存活对象后面的所有空间释放。(类似于顺序表删除中间元素,有元素搬运的过程)

在这里插入图片描述

3.2> 分代回收算法

分代回收算法的核心是:分代,具体逻辑是,认为存在的越久的对象,之后也很可能长期存在。

怎么判断对象存在时间的长短?
分代回收算法依据在垃圾回收中存活下来的次数,将堆分成了新生代和老年代两个大的部分。新生代又分为Eden(伊甸区)和s0,s1幸村区。如下图所示:

在这里插入图片描述

分代回收算法怎么进行垃圾回收?
①新new出来的对象,会在伊甸区Eden开辟空间,经过一轮的可达性分析后,会将垃圾对象清除,一般来说剩下的存活对象就很少了,将存活的对象放入幸存区S1
②在幸存区S1中又经过一轮垃圾回收考验存活下来的对象则拷贝到S2,然后在S2中又经过一轮垃圾回收考验存活下来的对象则拷贝到S1,如此反复,达到一定次数的垃圾回收在幸存区还存活下来的对象就将它放到老年区。
③在老年区中,则会放缓垃圾回收的频率,如果垃圾回收扫描时,扫到了垃圾,使用标记整理进行垃圾回收。

分代回收算法是"复合型"算法,体现在哪里?
在幸存区中,进行s1和s2的轮换着复制,是复制算法,但是由于空间小,复制成本不高。在老年代中,使用标记整理的方式,由于幸存到老年代的对象其实很少,所以标记整理的整理成本也不高。

3.3> 垃圾回收器

实际上,我们所说的这些垃圾回收算法,在不同的垃圾回收器中有各自的实现。有的追求垃圾回收的效率高,有的追求垃圾回收时STW短等,具体看业务场景而定。

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

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

相关文章

物联网技术的智能配电室综合监控系统设计

安科瑞电气股份有限公司 上海嘉定 201801 摘要:配电室是电力系统的重要基础设施,可以保障供配电系统的安全稳定运行。但是,配电室数量多、部署分散、管理复杂,人工现场巡检管理方式费时费工、错误率高,如果发生故障隐…

智能工厂-亿发生产型企业信息化ERP管理系统,打造智能制造工业

对于制造业来说,生产就是发展的动力。传统制造工厂在生产管理中存在很多不足和缺点,流程管理混乱,生产状态不稳定。在制造工厂中实施机械生产工厂ERP管理系统可以解决生产管理中低效率问题,使得制造工厂向着更科学化、智能化的方向…

ETHERNET/IP转RS485/RS232网关profinet与Ethernet通讯卡

网络数据传输遇到的协议不同、数据互通麻烦等问题,一直困扰着大家。然而,现在有一种神器——远创智控YC-EIP-RS485/232,它将ETHERNET/IP网络和RS485/RS232总线连接在一起,让数据传输更加便捷高效。 那么,它是如何实现…

Jmeter接口关联(一)【使用json层级方式提取值】与python中使用层级方式提取值 完成接口关联

文章目录 前言一、按照 json 的路径来提取 ​​​​​​​(1)成功匹配到数据的案例(按照层级匹配)(2)失败未匹配到数据的案例(没有按照层级匹配)json提取器二、使用完整的接口关联&a…

地方废物回收机构管理系统

方废物回收机构管理系统的开发运用java技术,MIS的总体思想,以及MYSQL等技术的支持下共同完成了该系统的开发,实现了地方废物回收机构的信息化,使用户体验到更优秀的地方废物回收机构管理,管理员管理操作将更加方便&…

奔赴猿田地,开垦一亩三分产

点击上方蓝色字体,选择“设为星标” 回复”云原生“获取基础架构实践 大家好,很久没开荒了,最近一直在想着要不要开始分享最近的一些小技术,看到互联网泡沫化日益严重,程序猿下岗失业,bibi皆是。但不管外界…

Linux如何设置固定IP

首先,Linux的默认网关和DNS解析都是一个地址。 IPV4 IP地址是32位2进制 前缀默认是24但是格式还是得写 DNS解析域名,类似Map,根据域名寻找IP DHCP 如果是自动设置IP,就是由他来自行分配,但是手动的话,就需…

软件测试-基础篇

一、接口的认识 什么是接口 电脑:USB,投影机(数据传输) 软件:API,application program interface.微信提现与充值,支付宝支付,银联支付接口。(鉴权码:token…

港联证券|暑期亲子游和避暑游需求旺盛 数据要素产业价值有望加速释放

上周五,A股再度回调,沪指失守3200点,创业板指、科创50指数跌约1%。截至收盘,沪指跌0.28%报3196.61点,深成指跌0.73%报10888.55点,创业板指跌1.05%报2169.21点,科创50指数跌1.02%;两市…

Vue生态及实践 - Nuxt

Nuxt.js Nuxt.js - Vue.js 通用应用框架 | Nuxt.js 中文网 Nuxt.js 中文教程_w3cschool 开源社区仁人志士创建了开箱可用的:Nuxt.js框架,帮助我们更快的实现ssr的同构。 Nuxt.js是一个基于Vue.js的通用应用框架它是对客户端,服务端基础架…

LeetCode 打卡day59--单调栈

一个人的朝圣 — LeetCode打卡第59-60天 知识总结 Leetcode 739. 每日温度题目说明代码说明 Leetcode 496. 下一个更大元素 I题目说明代码说明 Leetcode 84. 柱状图中最大的矩形题目说明代码说明 知识总结 今天做了单调栈的三道题 总结了一个模版套路: 寻找下一个更大的数 f…

Spring IOC - Bean的扫描

Component及其衍生注解:Configuration、Controller、Service、Repository标记的类,被Spring IOC扫描到后,即可被容器管理起来。其原理基本涵盖在AnnotationConfigApplicationContext构造函数体的三行代码里。 public AnnotationConfigApplic…

学校一键式报警器如何使用

学校一键式报警器通常是在紧急情况下使用的,例如火灾、恶性事件等。以下是一般的使用方法:1. 紧急情况发生时,发现危险或有人身安全受到威胁,迅速找到一键式报警器。2. 按下报警器上的按钮,通常是一个明显的红色按钮。…

查看docker运行状态,与查看防火墙运行状态

安装docker这里不细述了,可以通过 docker -version 查看安装的版本,出现成功就表示安装是ok的 查看docker状态是否启动状态,出现running就表示成功 systemctl status docker 如果没有则需要输入启动命令来启动 systemctl start docker 没报错…

前端学习——Web API (Day2)

Dom事件基础 事件监听 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"width…

干货 | 联通政企数据运营体系建设

以下内容整理自大数据能力提升项目必修课《大数据系统基础》同学们的期末答辩汇报。 我们将从以下几个方面为大家介绍我们的项目&#xff0c;首先第一部分是需求分析&#xff0c;然后是数据提取及处理&#xff0c;接着样本定义与分布、特征粗筛与模型选择、特征精筛与评分卡建模…

Java性能权威指南-总结28

Java性能权威指南-总结28 数据库性能的最佳实践Lambda表达式和匿名类Lambda表达式与匿名类加载 数据库性能的最佳实践 Lambda表达式和匿名类 对很多开发者而言&#xff0c;Java 8最激动人心的特性就是加入了Lambda表达式。不可否认&#xff0c;Lambda对Java开发者的开发效率有…

mac电脑 flv转mp4怎么转

mac电脑 flv转mp4怎么转&#xff1f;相信大家平时在电脑上下载视频的时候遇到过这样一个尴尬的事情&#xff0c;下载下来的视频不能被直接打开播放&#xff0c;而是需要使用专门的播放器才能打开查看&#xff0c;例如flv就是这样一种视频格式。大家都知道视频文件的格式种类非常…

CoT及ReAct解密与实战(三)

第8章 CoT及ReAct解密与实战 8.5 ReAct及计划和执行案例实战 我们来看一下LangChain的官方文档,首先它很简单的说了一下,计划和执行代理(Plan and execute agents)首先计划要做什么,然后执行子任务来实现目标,言外之意ReAct不是这样的,我们在ReAct中看见的内容是,有一步…

Debian使用Tomcat实现国密访问

环境准备&#xff1a; Debian 10 java version "1.8.0_131 Apache Tomcat/9.0.76 360国密浏览器 一.下载并安装jdk8 1.访问网站下载jdk8 https://www.oracle.com/java/technologies/downloads/#java8 2.解压到相应目录 3.配置环境变量 vim ~/.bashrc# java export JAV…