Java虚拟机 - JVM

JVM的内存区域划分

JVM它其实也是一个进程,进程运行的过程中,会从操作系统中申请一些资源.内存就是其中的一种.这些内存就支撑了java程序的运行.JVM从系统中申请的一大块内存,会根据实际情况和使用用途来划分出不同的空间,这个就是区域划分.它一般分为 堆区, 栈区, 程序计数器, 元数据区4个区域.

堆区

我们代码中new出来的对象就是在堆当中,对象持有的非静态成员变量,也是在堆当中.

栈区

栈区分为本地方法栈和虚拟机栈. 本地方法栈是jvm通过c++写的代码的调用关系和局部变量.一般我们只关心虚拟机栈. 虚拟机栈是记录了java代码的调用关系和java代码的局部变量.

程序计数器

这是一块小区域,专门用来存储下一条要执行的java指令的地址,和x86cpu中的eip寄存器差不多.

元数据区

在之前也叫做 "方法区". 它里面记录的是类的信息,方法的信息, 一个程序有哪些类,每个类有哪些方法,方法里面都会包含哪些指令,都会记录在元数据区中.想我们的代码中的各种逻辑都会转化为java字节码,这些字节码就会在程序运行的时候被jvm加载到内存中,放到方法区当中.程序怎么执行就会按照元数据区中的java字节码来依次执行.

注意: 这里堆和元数据区只有一份,但是栈区和程序计数器由N份,和线程数目相关.

JVM的类加载机制

这里的类加载就是java进程来运行的时候,需要把.class文件从硬盘读取到内存,且进行一系列的校验解析的过程. 这里加载的过程可以分为5个步骤. 加载 - 验证 - 准备 - 解析 - 初始化.

加载

大概就是把硬盘中的.class文件找到,打开文件且读取到文件内容.

验证

把当前读到的文件内容需要确保是合法的.class文件格式.

准备

给类对象申请内存空间.这里的申请到的内存空间,里面的默认值都是0.类对象中的静态成员变量的值也就是0)

解析

这里就是对类中的字符串常量进行处理.将常量池中的符号引用转换为直接引用的过程.(符号引用可以理解为字符串和它的引用在文件中有偏移量,直接引用则就是地址).

初始化

针对类对象完成后续的初始化过程(这里还会执行静态代码块的逻辑,父类的加载)

双亲委派模型(加载环节)

这里描述了怎么样来查找.class文件的策略.在我们的JVM中进行类加载的操作,会有一个专门的模块 - "类加载器(ClassLoader)". JVM中有三个ClassLoader.这里类加载器的作用就是给它一个带有包名的类名,来找到对应的.class文件.

三个库的作用:

BootstrapClassLoader: 负责查找标准库中的目录.

ExtensionClassLoader: 负责查找扩展库中的目录.

ApplicationClassLoader: 负责查找当前项目的代码目录和第三方库.      

这三个加载器的关系类似于父子关系.

工作过程

1. 先以ApplicationClassLoader为入口,开始工作.,但是它不会立刻开始查找自己负责的目录,而是将任务交给它的父亲.

2. 任务到ExtensionClassLoader,它也不会立刻开展工作,而是交给它的父亲.

3. 任务到BootsstrapClassLoader,它才会开始真正的搜索负责的目录.通过全限定类名,来尝试在标准库中找到符合的.class文件.找到了就进入打开文件,读文件的流程.没找到则交给它的孩子来处理.

4. ExtensionClassLoader拿到父亲给它的任务后,也会通过全限定类名,在扩展库中查找符合的.class文件.找到了就进入读文件的流程.没找到再交给它的孩子处理.

5. ApplicationClassLoader拿到夫妻给它的任务后,也会通过全限定类名,在项目中的目录和第三方库的目录中查找.找到了就进入读文件流程.没找到说明加载失败.则会抛出ClassNotFoundException异常.

这样设定的目的其实最主要的就是确保这几个类加载器的优先级. 按照这样的方式就算你自己写的类不小心和标准库中的类名字重复了,也可以避免导致标准库中的类失效.

JVM的垃圾回收机制(GC)

JVM的垃圾回收,说的就是将内存回收.在JVM的多个区中. 程序计数器不需要GC,栈不需要,因为它的局部变量在代码块执行结束后就会进行自动销毁. 元数据区也不需要,它一般都是涉及到类加载,很少涉及到类卸载. 这里最主要使用GC的就是堆区.因为它里面记录的是对象,而代码中可能会new出许许多多的对象,有的对象可能不使用了,就需要销毁.所以这里的内存回收,也可以看成是对象回收.

垃圾回收分为两步:

1. 识别出垃圾,哪些对象是垃圾,哪些对象不是垃圾.

2. 把标记为垃圾的对象的内存空间进行释放.

识别垃圾

这里识别一个对象是不是需要继续使用,就是判断还有没有引用在指向它.如果没有,它就可以视为垃圾.这里有两种方法: 1. 引用计数 2. 可达性分析

引用计数

这个方法在JVM中并没有使用,但是在其他的主流语言中还是在广泛应用中.

它的处理方式就是:

给每一个对象安排一个额外的空间,这个空间里就保存当前有几个引用. 而另一边会有专门的扫描线程,去获取到当前每个对象的引用计数的情况,一但发现这个对象的引用计数为0,就代表可以释放了.

但是它会存在一些问题:

1. 会消耗额外的内存空间.尤其是当对象比较小时,你的计数器消耗的空间可能就达到对象的一半了.

2.引用计数可能会产生循环引用的问题.

可达性分析(JVM使用的方法)

它的本质就是用时间来换空间. 它就不会产生额外的空间和循环引用的问题.

在代码中,会定义出很多变量,栈上的局部变量,堆上的成员变量,方法区的静态变量,常量池中引用的对象... 它就可以从这些变量作为起点,去进行遍历.这个遍历就是沿着这些变量持有的引用类型的成员,再进行下一步的访问. 可以遍历到的对象就不是垃圾,反之.

这里的遍历我们可以想象为二叉树的遍历.一但有一个节点为null,那这个结点后面的也都为null,也就是垃圾.

JVM中会存在扫描线程,对代码不断进行遍历.

清理垃圾

清理垃圾就是将被标记的内存空间进行释放.这里释放有三种方法.

标记 - 清除

这里就是直接将标记为垃圾的对象直接释放掉.但是一般不会使用这个方案.因为它会有内存碎片问题.直接释放,就会产生很多小的,离散的空闲内存空间,这就可能导致后续的内存申请失败.因为内存申请一般都是申请一块连续,大范围的空间.

复制

它的核心就不是直接释放内存,也是将不是垃圾的对象,复制到另一半的空间中(它会将可用内存空间分为两半). 

这样子就规避了内存碎片化的问题,但是也会有一些缺点.

1. 总用的内存空间变少了.

2. 如果每次需要复制的对象比较多,复制开销就会很大了(它适合大部分对象都释放,少部分对象存活,这个时候使用复制)

标记 - 整理

这个方式就类似于顺序表中的删除中间元素,需要进行搬运.通过这个过程,也就解决了内存碎片的问题,但是它这里的搬运内存开销会很大.

JVM的分代回收

 因为上面方法的优缺点,JVM就结合上面的思想,搞出来了一个综合方案.

JVM会给对象进行年龄记录,被JVM的扫描线程扫描过一次后就加一岁,起始岁数为0. 而还给堆内存划分了两个区域:分为为新生代和老年代.且新生代中还分了三个区:伊甸区和两个生存区.

 处理流程:

1. 当代码中new出一个新的对象时,就是创建在伊甸区中的,伊甸区中会有很多对象.(放在里面是有一个经验规矩: 伊甸区的对象大多都是活不过第一轮GC的,"朝生夕死")

2. 当第一轮GC后,少部分存活下来的对象,就会通过复制算法拷贝到生存区中(GC后存活下来的对象不会很多,复制开销也不会很大).后续还会有GC扫描,不仅会扫描伊甸区,还会扫描生存区.生存区的大多数对象也会在扫描中被标记为垃圾.少部分存活的,又会继续复制算法拷贝到另一个生存区汇中.这样不断往复,且每过一次GC,生存下来的对象年龄就会+1.

3. 当一个对象在生存区中经历了若干次GC还没有消亡,则JVM就会认为它的生命周期特别长,就会将它从生存区拷贝到老年代.

4. 老年代的对象也会被GC扫描,但是扫描频率会大大降低.

5.当老年代的对象消亡时,JVM就会按照标记整理的方式,释放内存(老年代的对象很少,所以搬运开销不会很大)


到这里,上面的就是JVM中GC的核心思想.但是在一些实现细节上还是会有一些变数和优化~

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

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

相关文章

springboot240基于Spring boot的名城小区物业管理系统

基于Spring boot的名城小区物业管理系统的设计与实现 摘要 当下,正处于信息化的时代,许多行业顺应时代的变化,结合使用计算机技术向数字化、信息化建设迈进。以前相关行业对于物业信息的管理和控制,采用人工登记的方式保存相关数…

InnoDB存储引擎对MVCC的实现

MVCC MVCC的目的 在搞清楚MVCC之前,我们要搞懂一个问题,MVCC到底解决的是什么问题? 我用一句话概括,那就是为了解决读-写可以一起的问题! 在我们的印象里,InnoDB可以读读并发,不能读写并发,或者写写并发 这是很正常的想法,因为如果读写并发的化,会有并发问题 而对于写写…

构建安全的REST API:OAuth2和JWT实践

引言 大家好,我是小黑,小黑在这里跟咱们聊聊,为什么REST API这么重要,同时,为何OAuth2和JWT在构建安全的REST API中扮演着不可或缺的角色。 想象一下,咱们每天都在使用的社交媒体、在线购物、银行服务等等…

Sqli-labs靶场第16关详解[Sqli-labs-less-16]自动化注入-SQLmap工具注入

Sqli-labs-Less-16 #自动化注入-SQLmap工具注入 SQLmap用户手册:文档介绍 - sqlmap 用户手册 以非交互式模式运行 --batch 当你需要以批处理模式运行 sqlmap,避免任何用户干预 sqlmap 的运行,可以强制使用 --batch 这个开关。这样&#xff0…

GC机制以及Golang的GC机制详解

要了解Golang的GC机制,就需要了解什么事GC,以及GC有哪几种实现方式 一.什么是GC 当一个电脑上的动态内存不再需要时,就应该予以释放,以让出内存,这种内存资源管理,称为垃圾回收(Garbage Collection)&#x…

最长上升子序列(LIS)简介及其例题分析

一.最长上升子序列(LIS)的相关知识 1.最长上升子序列(Longest Increasing Subsequence),简称LIS,也有些情况求的是最长非降序子序列,二者区别就是序列中是否可以有相等的数。假设我们有一个序…

【论文笔记】Initializing Models with Larger Ones

Abstract 介绍权重选择,一种通过从预训练模型的较大模型中选择权重子集来初始化较小模型的方法。这使得知识从预训练的权重转移到更小的模型。 它还可以与知识蒸馏一起使用。 权重选择提供了一种在资源受限的环境中利用预训练模型力量的新方法,希望能够…

【Linux】软件管理yum | 编辑器vim | vim插件安装

目录 1. Linux软件管理yum 1.1 什么是软件包 1.2 查看软件包 1.3 如何安装软件 1.4 如何卸载软件 2. Linux编辑器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2.6 插件安装 1. Vim-Plug 3. coc.nvim …

如何自己系统的学python

学习Python是一项很好的投资,因为它是一种既强大又易于学习的编程语言,适用于多种应用,如数据分析、人工智能、网站开发等。下面是一个系统学习Python的步骤建议: 基础准备 安装Python: 访问Python官网下载最新版本的…

微信小程序-生命周期

页面生命周期 onLoad: 页面加载时触发的方法,在这个方法中可以进行页面初始化的操作,如获取数据、设置页面状态等。 onShow: 页面显示时触发的方法,在用户进入页面或从其他页面返回该页面时会调用此方法。可以在此方法中进行页面数据刷新、动…

Onenote软件新建笔记本时报错:无法在以下位置新建笔记本

报错现象: 当在OneNote软件上,新建笔记本时: 然后,尝试重新登录微软账户,也不行,提示报错: 解决办法: 打开一个新的记事本,复制粘贴以下内容: C:\Users\Adm…

Mysql中的事务

什么是事务: 多条sql语句,要么全部成功,要么全部失败。 事务的特性: 1:原子性(Atomic): 组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有操作都成功,整个事务才会…

C++调用lua函数

C 调用Lua全局变量(普通) lua_getglobal(lua, "width");int width lua_tointeger(lua,-1);lua_pop(lua,1);std::cout << width << std::endl;lua_close(lua); 这几行代码要放到lua_pcall(lua, 0,0,0);之后才可以. C给lua传递变量 lua_pushstring(lua, …

Python 操作 Excel,如何又快又好?

➤数据处理是 Python 的一大应用场景&#xff0c;而 Excel 则是最流行的数据处理软件。因此用 Python 进行数据相关的工作时&#xff0c;难免要和 Excel 打交道。Python处理Excel 常用的系列库有&#xff1a;xlrd、xlwt、xlutils、openpyxl ◈xlrd &#xff0d; 用于读取 Exce…

Rocky 9 安装 R-CytoTRACE

官网给出的详细指南&#xff0c;只是可能大家打不开或者懒得去看E文。 第一步&#xff0c;下载CytoTRACE安装包。 wget https://cytotrace.stanford.edu/CytoTRACE_0.3.3.tar.gz 第二步&#xff0c;打开R或者Rstudio-server # 安装依赖包 if (!requireNamespace("Bioc…

视频云平台——搭建SRS5平台支持GB28181视频流的推送

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不仅…

谨用ArrayList中的subList方法

谨用ArrayList中的subList方法 规范一&#xff1a; ArrayList 的 subList 结果不可强转成 ArrayList&#xff0c;否则会抛出 ClassCastException 异常&#xff1a; public static void test7() {List<Integer> list new ArrayList<>();list.add(1);list.add(2);…

JavaWeb—— SpringBootWeb综合案例(登录功能、登录校验、异常处理)

案例-登录认证 目录 案例-登录认证1. 登录功能1.1 需求1.2 接口文档1.3 思路分析1.4 功能开发1.5 测试 2. 登录校验2.1 问题分析2.2 会话技术2.2.1 会话技术介绍2.2.2 会话跟踪方案2.2.2.1 方案一 - Cookie2.2.2.2 方案二 - Session2.2.2.3 方案三 - 令牌技术 2.3 JWT令牌2.3.1…

程序员眼中的“祖传代码”

引言 在IT界&#xff0c;特别是在Java项目中&#xff0c;“祖传代码”通常指的是那些经过长时间积累、由多位开发者共同维护、且蕴含深厚技术沉淀的代码片段或模块。这些代码可能存在于项目的核心模块&#xff0c;也可能是一些辅助性的工具类。它们承载着项目的历史&#xff0…

Matlab 多项式插值(曲线拟合)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 由于对曲线拟合有些兴趣,这里就找了一些资料从最基本的方法来看一下曲线拟合的效果: 二、实现代码 % **********