【JavaEE】简单了解JVM

目录

一、JVM中的内存区域划分

二、JVM的类加载机制

1、类加载的触发时机

2、双亲委派模型 

1.1、向上委派

1.2、向下委派

三、JVM中的垃圾回收机制(GC)

1、确认垃圾

1.1、引用计数(Java实际上没有使用这个方案,但是Python、PHP采用了)

1.1.1、循环引用 

1.2、可达性分析(被Java采用)

2、释放内存 

2.1、标记清除

2.2、复制算法

2.3、标记整理 

2.4、分代回收


一、JVM中的内存区域划分

JVM就是一个Java进程,Java进程会从操作系统中申请一大块内存区域,给Java代码使用。这里申请的一大块内存区域又被进一步划分,给出了不同的用途。

  1. 堆:存放new出来的对象(也就是说对象中成员变量在堆上)
  2. 栈:用来维护方法之间的调用关系(也就是说方法中的局部变量在栈上)
  3. 方法区(Java8之前的叫法)/元数据区(Java8开始的叫法):存放的是类加载之后的类对象(类名.class)。也就是存在被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据的。

❌❌这里需要明确的一点,我们在网上看到的有些资料中,说到内置类型的变量是在栈上,引用类型的变量是在栈上。这个说法是错误的

  • 这里变量在堆上还是在栈上,与这个变量是什么类型无关,如果你在类中定义了一个内置类型的变量,通过new关键字,创建这个类的对象,那么这个内置类型的变量,就是在堆上。
  • 判断一个变量在堆上还是在栈上,需要关注的是这个变量是局部变量还是在成员变量如果是局部变量则这个变量在栈上。如果是成员变量则这个变量在堆上
public class Test{public int a;public String b;
}
class Test1{public static void main(String[] args){Test s = new Test();}
}
//通过上述的代码,可以看见定义的int类型的变量a是一个内置类型的变量,但是他是一个成员变量,所以它在堆上。
//在main方法中的s变量是对象的引用,他是一个引用类型的变量,他在main方法内部是一个局部变量,所以变量s在栈上。

 ​​​

  1. 虚拟机栈早期也叫Java栈,每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应这一次次的Java方法调用虚拟机栈是给Java代码使用的。主管Java程序的运行,它保存方法的局部变量。部分结果,并参与方法的调用和返回
  2. 本地方法栈:是给jvm内部的本地方法使用的。(jvm内部是通过C++代码实现的方法)。
  3. 程序计数器:是用来记录当前程序执行到那个字节码指令了。

堆和元数据区(方法区)在一个JVM进程中只有一份。但是栈(本地方法栈和虚拟机栈)和程序计数器则是存在多份,每个线程中都会存在一份(栈和程序计数器)。

二、JVM的类加载机制

把.class文件加载到内存得到类对象的过程称为类加载。因为程序要执行,就需要把依赖的"指令和数据"加载到内存中。类加载的过程有5个步骤:加载、验证、准备、解析、初始化。

1️⃣加载阶段(Loading)

这个阶段是找到.class文件,并且读取文件内容。这里找class文件的方法中最常问到的是双亲委派模型。

2️⃣验证阶段(Verification)

这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。

3️⃣准备阶段(Preparation)

给类对象分配内存空间,这里分配的内存空间是未初始化的空间,内存空间中的数据是全0的。

4️⃣解析阶段(Resolution)

针对字符串常量进行初始化。也就是Java虚拟机将常量池内的符号引用替换为直接引用的过程。

  • 符号引用:字符串常量本身在class文件中存在,但是因为他是在文件中,还没有在内存中,并不直到自己的地址,只能使用一些特殊符号来占位。这就得到了符号引用。
  • 直接引用:将字符串常量加载到了内存中,就会把字符串常量填充到内存中的特定地址上,但是字符串在内存中的相对位置与class文件中相对位置还是不变的。

5️⃣初始化阶段(Initialization)

针对类对象进行初始化。初始化静态成员变量、执行静态代码块、如果一个类存在父类,还需要加载父类。

1、类加载的触发时机

类加载不是JVM一启动,就会把所有的.class文件都在加载了,他是一个"懒加载"的策略,也就是非必要不加载。

只有在下面的情况下才会触发类加载

  1. 创建了这个类的实例,这个时候就会触发类加载
  2. 虽然没有创建这个类的实例,但是使用了这个类的静态方法或者静态属性就会触发类加载
  3. 使用子类,会触发父类的加载

2、双亲委派模型 

双亲委派模型正对的是Java虚拟机中三个类加载器的,这三个加载器分别是:

  • 启动类加载器(BootStrap ClassLoader):负责加载Java标准库中的类
  • 扩展类加载器(Extension ClassLoader):负责加载一些非标准库的类,是由Sun/Oracle扩展的库的类。(因为Java最初是是属于Sun公司的但是最后被Oracle公司收购)
  • 应用程序类加载器(Application ClassLoader):负责加载项目中自己写的类以及第三方库中的类。

 当具体加载一个类的时候,它的过程是:需要先给定一个类的权限定类名"java.lang.String"(字符串)。然后是向上委派,在然后是向下委派

1.1、向上委派

然后从Application ClassLoader这个类加载器开始,收到类加载的请求后,不会自己记载这个类,而是把这个类加载请求向上委派给它的父类去完成,父类收到这个请求后又继续向上委派给自己的父类,一次类推,直到所有的请求委派到启动类加载器中。

1.2、向下委派

BootStrap ClassLoader接收到类加载请求后,先是在自己负责的范围内查找,如果搜索到,就直接进行后续加载步骤(验证、准备、解析、初始化),如果没有搜索到这个类,父类会把这个信息返回交给孩子(Extension ClassLoader)来处理,直到这个请求被成功加载,但是一直到自定义加载器都没有找到,JVM就会抛出ClassNotFund异常

 

三、JVM中的垃圾回收机制(GC)

JVM的垃圾回收机制是帮助程序员自动释放内存的。内存释放这是一个比较难把控的事情,因为申请内存的时机是明确的,使用到了就必须申请,释放的时候是模糊的,彻底不使用了才能释放。这就特别依赖程序员的水平,就像C/C++程序员他们释放内存就需要自己手动释放。但是Java通过JVM自动判定,基于一系列策略,就可以让这个准确性比较高。这里我们说的释放内存空间指的就是释放堆上的申请的空间。

垃圾回收主要分为两个阶段:

  1. 确认垃圾:找没有被引用的对象
  2. 释放垃圾:将找到的对象释放掉 

如何判断一个对象是否有引用指向,这里有两个方法:引用计数和可达性分析。

1、确认垃圾

1.1、引用计数(Java实际上没有使用这个方案,但是Python、PHP采用了)

 

✨这个引用计数的方法存在两个缺陷。

  1. 浪费内存空间,空间利用率不高,引用计数占用内存空间。
  2. 存在循环引用的情况 ,会导致引用计数的判断逻辑出错

1.1.1、循环引用 

当new了两个对象,正常情况下两个对象的引用各自指向引用的对象,但是这个两个对象中的成员变量相互指向对方对象,这个时候,两个对象中的计数器在各自加1.

 这个时候,t和t1不在指向之前的引用,两个对象的引用计数都减1,但是这两个对象的引用计数并没有被清空,实际上这两个对象已经不被使用了,但是无法被当作垃圾,无法释放。这两个对象也无法再次被使用,想要使用一个对象,就需要找到另一个对象,这就在逻辑上形成了循环。

1.2、可达性分析(被Java采用)

把对象之间的引用关系理解成了一个树形结构,从一些特殊的起点出发,进行遍历,只要能遍历访问到的对象,就是"可达的",再把"不可达的"当作垃圾即可。

 ✨可达性分析的关键要点,进行上述遍历,需要有"起点"被称为gcroots

  1. 栈上的局部变量(每个栈的每个局部变量,都是起点)
  2. 常量池中引用的对象
  3. 方法区中,静态成员引用的对象

可达性分析,总的来所,就是从所有的gcroots的起点出发,看看该对象里又通过引用能访问那些对象,依次遍历,把所有可以访问的对象都给遍历一遍(遍历的同时把对象标记成"可达"),剩下的遍历不到的对象就是"不可达".

🍂可达性分析的特点:可达性分析克服了引用计数的两个缺点,但是也有自己的问题

  1. 消耗的时间更多,因此某个对象成了垃圾,也不一定能第一时间发现,因为扫描的过程,需要消耗时间
  2. 在进行可达性分析的时候,依次遍历,一旦这个过程中,当前代码中的对象引用关系发生了变化,这就会使情况变得更加复杂。比如,当一个对象指向下一个对象,刚遍历完这个对象,这个对象的引用变了。因此,我们为了更准确的遍历,需要让其他的业务线程暂停工作(STW问题)。

2、释放内存 

这里存在三个典型的策略:

  1. 标记清除
  2. 复制算法
  3. 标记整理
  4. 分代回收

前三种释放内存的方式都存在一定的缺点,所以实际上JVM的实现思路,是结合了上述的几种思想方法。

2.1、标记清除

这种策略,就是直接把垃圾对象的内存释放,但是这个方式的缺点就是会产生内存碎片。

我们从内存中申请空间的时候,都是整块的连续的空间,现在这里空闲的空间是离散的,独立的空间,总的空间可能很大。假如总的空闲的空间可能超过了1G,但是你想申请500MB可能都不一定申请到。

2.2、复制算法

为了解决内存碎片的问题,又引入了复制算法。复制算法,十八整个内存空间,分成两半,一次只用一半。

 复制算法解决了内存碎片化的问题,但是也有缺点

  1. 内存利用率比较低
  2. 如果当前的对象大部分都是要留的,垃圾很少,此时复制成本就比较高

2.3、标记整理 

 这个方法,也可以结局内存碎片化的问题,但是他的搬运开销还是比较大的。

2.4、分代回收

针对不同的情况,使用不同的策略。给对象设定"年龄"这样的概念(当然这个单位并不是年龄,这里用年龄作为类比),描述了这个对象存在多久了,如果一个对象刚创建,认为是0岁,没经过一轮扫描(可达性分析),没有被标记成垃圾,这个时候对象就涨一岁,通过这个年龄来区分这个对象的存活时间。

🍂根据年龄的大小来区分采用什么样的策略 

 

  1. 新城建的对象,放到伊甸区,当垃圾回收扫描到伊甸区之后,绝大部分对象都会在第一轮GC中被回收
  2. 如果伊甸区的对象,在第一轮的GC中没有被回收,就会通过复制算法,拷贝到幸存区,幸存区分成了大小均等的两部分,一次只使用其中的一半。垃圾回收扫描幸存区的对象,发现垃圾,通过复制算法,将不是垃圾的对象复制到生存区的另一半空间中,然后将前一半的空间全部释放。
  3. 当这个对象在生存区,经过了若干轮GC之后,年龄增长到一定的程度,就会通过复制算法拷贝到到年代。
  4. 进入老年代的对象,年龄都很大了,在消亡的概率就比前面的新生代中的对象小了很多,针对老年代GC的扫描频次就会降低很多。如果老年代中出现了垃圾对象,就会使用标记整理的算法进行清理,因为老年代中消亡的对象就很少了,所以使用标记整理的算法开销还不是很大。
  5. 特殊情况:如果对象非常大,直接进入老年代(大对象进行复制算法,成本比较高,而且大对象也不是很多)

根据经验规律:如果一个对象存活的时间很长了,他将继续存在更长的时间。

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

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

相关文章

【HttpRunnerManager】搭建接口自动化测试平台实战

目录 一、需要准备的知识点 二、我搭建的环境 三、搭建过程 四、访问链接 五、两个问题点 【整整200集】超超超详细的Python接口自动化测试进阶教程,真实模拟企业项目实战!! 一、需要准备的知识点 1. linux: 安装 python3、nginx 安装和…

emWin - BMP图片显示

BmpCvt.exe 用途 利用BMP图片,进行GUI显示;ICON等图标都是小BMP图片,核心是将BMP图片,转成emWin支持的方式,最终显示到TFT屏上 使用BmpCvt.exe工具,将各个图片转成相应的C文件. emWin有关的工具&#xff…

Redis的数据淘汰策略

目录 一、概念 二、redis有哪些数据淘汰策略 三、注意事项 四、怎么选择数据淘汰策略 五、总结 一、概念 Redis的数据淘汰策略指的是在内存不足的情况下,如何决定哪些数据需要被淘汰以释放内存空间,当Redis中的内存不够用时,此时在向Red…

测试|Selenium介绍及环境搭建

测试|Selenium介绍及环境搭建 1.Selenium是什么 Selenium是用来做web网站 UI自动化的测试工具/测试框架。 我们这里说的Selenium是Selenium2.0,它由Selenium IDE,Webdriver, Selenium Grid组成。 Selenium IDE是用于Selenium测试的完成集成开发环境&…

云计算——云计算关键技术

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​ 目录 前言 一.云计算关键技术 1.虚拟化技术 2.分布式数据存储技术 (1&…

ubuntu 相关命令记录

检查ssh 是否可用 ssh 安装curl apt install curl 进入root 进入root 账号 sudo -i 修改密码 sodo passwd 开启root 可远程连接 修改SSH配置文件 可以通过SSH配置文件更改包括端口、是否允许root登录等设置,配置文件位置: /etc/ssh/sshd_config…

天下风云出我辈,AI准独角兽实在智能获评“十大数字经济风云企业

时值盛夏,各地全力拼经济的氛围同样热火朝天。在浙江省经济强区余杭区这片创业热土上,人工智能助力数字经济建设正焕发出蓬勃生机。 7月28日,经专家评审、公开投票,由中共杭州市余杭区委组织部(区委两新工委&#xff…

如何解决制造业数字化改造的障碍?

制造业的数字化转型可能是一个复杂且具有挑战性的过程,但解决以下障碍有助于为成功实施铺平道路: 抵制变革:数字化转型中最常见的挑战之一是员工的抵制,尤其是那些习惯传统方法的员工。为了克服这一问题,组织需要培养一…

JDBC-笔记

JDBC 1. JDBC介绍 JDBC(Java Database Connectivity)是一种用于连接和操作数据库的 Java API。 通过Java操作数据库的流程 第一步:编写Java代码 第二步:Java代码将SQL发送到MySQL服务端 第三步:MySQL服务端接收到SQ…

Spring中IOC容器常用的接口和具体的实现类

在Spring框架没有出现之前,在Java语言中,程序员们创建对象一般都是通过关键字new来完成,那时流行一句话“万物即可new,包括女朋友”。但是这种创建对象的方式维护成本很高,而且对于类之间的相互关联关系很不友好。鉴于…

获取安卓模拟器截图

最简单的方法当然是使用adb adb shell screencap -p >screenshot.png 不过有些app会限制adb进行截图,这个时候adb截屏为黑屏 此时有两种方法获取截图 第一使用模拟器自带截图快捷键,获取安卓自带截图键,逍遥模拟器为altF3 可模拟键盘输入按键&#…

OnTrigger的几种情况

在Unity中,OnTrigger是一种用于处理碰撞事件的函数。它通常用于监测对象之间的触发器(Collider)交互,并在特定的情况下触发相应的逻辑。在Unity中,有以下几种类型的OnTrigger事件:OnTriggerEnter、OnTrigge…

【Web】web

dns与域名 网络是基于tcp/ip协议进行通信和连接的 应用层——传输层——网络层——数据链路层——物理层 每一定的台主机都有一个唯一且固定的地址标识——IP地址 IP地址的做用:1.区分用户和计算机;2.进行通信 IP地址由32位二进制数组成,…

chatGPT——如何和一个很可爱,思维比较跳跃 ,活泼的女孩聊天

和一个可爱、思维跳跃、活泼的女孩聊天可以是一种很有趣的体验。以下是一些建议,帮助你在对话中保持积极、轻松和有趣的氛围: 用幽默感:幽默是促进轻松气氛的有效方式。适时地开玩笑,但要确保不会伤害对方感情或触及敏感话题。笑话…

读取application-dev.properties的中文乱码【bug】

读取application-dev.properties的中文编码【bug】 2023-7-30 22:37:46 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是https://blog.csdn.net/qq_51625007 禁止其他平台发布时删除以上此话 bug 读取application-dev.propert…

ATFX汇市月报:7月美联储坚定加息,8月成利率决议空档期

7月汇市行情回顾—— 7月份,美元指数下跌1.01%,收盘在101.88点, 欧元升值0.76%,收盘价1.0997点; 日元升值1.41%,收盘价142.27点; 英镑升值1.08%,收盘价1.2835点; 瑞…

TSINGSEE青犀视频AI算法引擎中台在渣土车智慧管控场景中的应用

一、行业背景 随着社会的发展和人们生活水平的不断进步,大家对环境卫生和空气质量的要求越来越重视。渣土车是建筑垃圾的运输主力,也存在行驶频繁、不合规、不合法的操作,可能对交通安全、环境卫生和城市形象造成影响。比如在施工工地&#x…

基于MATLAB实现图像处理常用应用案例(附上100个仿真源码+数据)

MATLAB是一款功能强大的图像处理软件,可以用于实现各种常见的图像处理应用。下面将介绍几个常见的图像处理应用案例。 文章目录 1. 图像去噪2. 图像增强3. 图像分割4. 特征提取5. 图像拼接6. 完整源码数据下载 1. 图像去噪 图像去噪是图像处理中的一项重要任务&am…

STM32 中断优先级管理(二)

NVIC中断管理相关函数主要在HAL库关键文件stm32f1xx_hal_cortex.c中定义。 中断优先级分组函数 void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);这个函数的作用是对中断的优先级进行分组,这个函数在系统中只需要被调用一次。 void HAL_NVIC_SetPrio…

【C++】C/C++内存管理

1、C/C内存分布 【1】栈又叫堆栈 – 非静态局部变量/函数参数/返回值等等,栈是向下增长的。 【2】内存映射段 – 是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信 【3】堆 – 用于程序运…