【笔记】深入理解JVM机制

news2024/9/23 22:52:16

  • 🎥 个人主页:Dikz12
  • 📕格言:吾愚多不敏,而愿加学
  • 欢迎大家👍点赞✍评论⭐收藏

目录

 JVM 运⾏流程图

 JVM 中内存区域划分

 方法区 / 元数据区

 栈

 程序计数器

 本地方法栈

内存区域总结 

 JVM 中类加载过程

双亲委派模型

JVM 中的垃圾回收机制

找到垃圾 

 引用计数 (Python,PHP)

 可达性分析(Java) 

回收垃圾

标记清除

 复制算法

标记整理

堆的新生代 和 老生代


 JVM 运⾏流程图

 JVM 中内存区域划分

一个运行起来的Java进程,就是一个JVM虚拟机,就需要从操作系统申请一块内存,然后把这个内存,划分成不同区域,每个区域都有不同的作用.

比如:学校这么大场地,就相当于申请了一大块内存空间.

 方法区 / 元数据区

jdk1.7及其以前叫做 方法区;从jdk1.8开始叫做元数据区.

元数据区存储内容:用来存储被虚拟机加载的类信息、常量、静态变量. (这里存储的是类对象, .class文件,加载到内存之后,就成了类对象)

堆存储的内容:程序创建的所有对象都保存在堆里.(就是代码new的对象)

堆⾥⾯分为两个区域:新⽣代和⽼⽣代.


 

具体在垃圾回收机制里说明!!!

 栈

 栈存储的内容:代码执行过程中,方法之间的调用关系.(a->b->c)

每个栈帧里就包含了方法的入口, 方法的返回的位置,方法的形参,方法的返回值,局部变量......等

 程序计数器

 程序计数器是一块比较小的空间,主要就是存放一个“地址”,下一条要执行的指令,在内存中的哪个地方.

 刚开始调用方法,程序计数器,记录的就是方法入口的地址;随着一条一条的执行指令,每执行一条,程序计数器的值就会自动更新,去指向下一条指令.

class Test{

    public void a(){...}
    public void b(){...}
}

这里的方法a 和 方法b都会被编译成二进制的指令,就会被放到.class文件中.

 本地方法栈

本地方法栈,指的是使用nativa关键字修饰的方法;这个方法不是使用Java实现的,而是在JVM内部通过C++代码实现的. (JVM内部的C++代码调关系) 

内存区域总结 

 

 JVM,是一个运行Java进程,一个进程可以包含多个线程,内存区域有如何划分?

虚拟机栈及程序计数器,都是每个线程都有一份.

堆和元数据区在jvm进程中只有一份,线程共享.

JVM 有10个线程,就会有10个虚拟机栈,也会有10个程序计数器.

 JVM 中类加载过程

对于一个类来说,它的生命周期如下:

 课本上 和 官方文档 把这个类加载的过程,主要是分成了五个部分.

1.加载.   找到.class文件,打开文件,读取文件内容. 代码中,会给定某个类的“全限定类名”,jvm就会根据这个类名,在一些指定的目录范围内查找.     (全限定类名:java.lang.Sring 、java.util.ArrayList)

2.验证.   检验 .class 文件是一个二进制的格式.(某个字节,都是有特定的含义)     

                           

java8规范要求:Chapter 4. The class File Format

 3. 准备.    给类对象分配内存空间,这里只是分配内存空间,还没有初始化呢,此时这个空间上的内存数值,全是0.

4.解析.     针对类对象中包含的字符串常量进行处理,进行一些初始化操作. java代码中用到的 字符串常量,在编译之后也会进入到.class文件中.

比如: final String str = "test".

在.class文件的二进制指令中,也会有一个 str 这样的引用被创建出来;由于应用本质上保存的是一个变量的地址,在 .class文件是一个文件,不涉及到内存地址,就会先设置成一个“文件的偏移量”通过偏移量,就能找到“test”这个字符串所在的位置.(真正被加载到内存的时候,再把这个偏移量替换成真正的内存地址)

5. 初始化.   针对类对象进行初始化,就是把类对象中需要的各个属性都设置好.

双亲委派模型

1.BootStrap :启动类加载器.
2. Ext ClassLoader :扩展类加载器。加载 lib/ext ⽬录下的类.
3. App ClassLoader:应⽤程序类加载器.
4. ⾃定义加载器:根据⾃⼰的需求定制类加载器.

 JVM中,内置了,三个类加载器:

过程: 

1. ⼀个类加载器收到了类加载的请求,它⾸先不会⾃⼰去尝试加载这个类.

2. ⽽是把这个请求委派给⽗类加载器去完成,每⼀个层次的类加载器都是如此.
3. 所有的加载请求最终都应该传送到最顶层的启动类加载器中
4. 只有当⽗加载器反馈⾃⼰⽆ 法完成这个加载请求(它的搜索范围中没有找到所需的类)时,⼦加载器才会尝试⾃⼰去完成加载.

5.当子类加载器AppClassLoader,也没找到就会抛出一个ClassNotFoundException.

JVM 中的垃圾回收机制

 GC垃圾 回收的目标,其实就是 内存中的对象. 对于Java来说就是 new出来的对象.

栈里放的是局部变量,是跟随栈帧的生命周期走的.(方法执行结束,栈帧销毁,内存自然释放)

静态变量,生命周期就是整个程序,始终在,就意味着,静态变量时无需释放的.

GC可以理解两大步骤:  1. 找到垃圾.        2.释放垃圾  

找到垃圾 

 引用计数 (Python,PHP)

为什么Java不使用引用计数???

1.比较浪费内存.

计数器,也要有两个字节.如果对象本身就很小,这个计数器占据的空间比例就很大了.

比如对象本身就两个字节,计数器占据的空间就是50%. (如果对象小并且多,计数器占据的空间就难以忽视了)

2.引用计数机制,存在“循环引用”问题.

 可达性分析(Java) 

 有一个 或者 一组线程,周期性的扫描我们代码中的所有对象. 

从一些特定的对象出发,尽可能的进行访问的遍历,把所有都能够访问到的队象,都标记成"可达"

反之,经过扫描之后,未被标记的对象,就是“垃圾”了.

可达性分析,是周期性进行的,所以是比较消耗系统资源,开销比较大.是时间换空间的手段 

回收垃圾

标记清除

简单粗暴的方式,比如,申请了一块内存空间,上面有一些对象,通过可达性分析发现 2 和 4是垃圾, 就直接把对应的对象的内存,直接释放掉,就是标记清除方案.

 这个方案其实非常不好,会产生很多的内存碎片;释放内存,目的是为了让别的代码能够申请. 申请内促,都是申请到“连续”的内存空间 ; 随着时间的推移,内存碎片的情况,就会越来越严重,最会导致后续内存申请举步维艰.

 复制算法

 通过复制的方式,把有效的对象,归类到一起,在统一释放剩下的空间.

比如,一块内存空间先一分为二,一次只用一其中一半, 里面有一些对象,假设1,3,5垃圾,通过复制算法,把2 和 4 复制到另外一边,就可以把左侧的整体释放掉.

 

 这个方案可以有效的解决内存碎片问题,缺点也是很明显的;

 1.内存要浪费一半,利用率不高.      

 2.如果有效对象非常多,复制的开销就很大了.

标记整理

 既能够解决内存碎片的问题,又能处理复制算法中的利用率.

过程就类似于顺序表删除元素的搬运操作. 

比如,一块内存空间,通过可达性分析,1,3,5是垃圾, 把2搬运到1的位置.... 

 

 

搬运开销仍然很大!!

实际上,JVM 采取的释放思路,是上述基础思路结合体.(让不同的方案,扬长避短) 

堆的新生代 和 老生代

垃圾回收只是针对堆进行的, 堆的内存空间会分成两部分,不是等分的,具体怎么分不一定,左边称为“新生代”,右边称为“老年代”.

新生代:又进一步的划分,分为 幸存区 和 伊甸区.

幸存区:等分的两部分,每次只用一块. 正好就是复制算法的体现.

伊甸区:放的是刚 new出来的对象.(还没有经过可达性扫描)

 

 经验规律: 从对象诞生,到第一轮可达性分析扫描,这个过程中虽然时间不长,基本就是 毫秒 - 秒(这个时间维度,对于程序的眼中也挺长了),在这个时间里大部分的对象都会成为垃圾.

 1.伊甸区 -> 幸存区 (复制算法)

       每一轮GC扫描之后,都会把有效的对象,复制到幸存区中,伊甸区就可以整个释放了. 由于经验规律,真正需要复制的对象不多,非常适合复制算法.

2. GC 扫描线程也会扫描 幸存区. 就会把活过GC扫描的可达对象,复制到幸存区的另一部分. 幸存之前的复制,每一轮会复制多个对象,每一轮也可以淘汰掉一些对象.

3. 当这个对象已经在幸存区存活很多轮GC扫描之后,JVM 就认为这个对象,短时间内是应该释放不掉,就会把这个对象复制到老年代.

 4. 进入老年代的对象,虽然也会被GC 扫描,扫描频率就会比新生代,低很多.

   (也是为了减少GC扫描的开销,要挂早就挂了!!!)

   

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

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

相关文章

flask_restful渲染模版

渲染模版就是在 Flask_RESTful 的类视图中要返回 html 片段代码,或 者是整个html 文件代码。 如何需要浏览器渲染模板内容应该使用 api.representation 这个装饰器来定 义一个函数, 在这个函数中,应该对 html 代码进行一个封装&#xff…

【Java程序设计】【C00368】基于(JavaWeb)Springboot的箱包存储系统(有论文)

TOC 博主介绍:java高级开发,从事互联网行业六年,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,博客中有上百套程序可供参考,欢迎共同交流学习。 项目简介 项目获取 🍅文末点击卡片…

编译u-boot(硬件: atk-dl6y2c)和NFS/EMMC模式启动Linux Kernel

目录 概述 1 编译u-boot 1.1 解压文件 1.2 编译u-boot 2 配置环境 2.1 在Ubunt 搭建TFTP 2.2 建立下载目录 3 烧写bootloader到SD 4 使用NFS模式启动板卡 5 从EMMC 启动 Linux 系统 5.1 通过配置参数方式 5.2 使用命令直接启动内核 文中使用的代码下载地址&#xf…

MySQL 8.x 高可用集群之MGR(组复制)

MySQL 8.x 高可用集群之MGR(组复制) 前言一、集群部署1.1 修改主机名和关闭selinux1.2 MySQL安装1.2.1 **移除Linux自带安装的mariadb数据库**1.2.2 下载安装文件1.2.3 顺序执行安装1.2.4 启动mysql并修改密码1.2.5 开启防火墙端口允许访问 1.3 修改mysq…

模仿羊羊~消消乐

慎玩!随机生成、不保证能消完哦! 游戏试玩: 链接: https://pan.baidu.com/s/1IwtOd__8Ca0bSouMP8kEzw 提取码: 6yhd

[深度学习]yolov8+pyqt5搭建精美界面GUI设计源码实现五

【简单介绍】 依托先进的目标检测算法YOLOv8与灵活的PyQt5界面开发框架,我们倾力打造出了一款集直观、易用与功能强大于一体的目标检测GUI界面软件。通过深度融合YOLOv8在目标识别领域的出色性能与PyQt5的精美界面设计,我们成功推出了一款高效且稳定的软…

【Web】浅聊Jackson序列化getter的利用——POJONode

目录 核心速览 原理分析 EXP TemplatesImpl利用 SignedObject利用 核心速览 writeValueAsString是jackson序列化自带的入口,在调用该方法的过程中将会通过遍历的方法将bean对象中的所有的属性的getter方法进行调用 下面介绍如下利用链: BadAttrib…

python3游戏GUI--开心打地鼠游戏By:PyQt5(附下载地址)

文章目录 一.前言二.游戏预览1.启动2.开始游戏3.游戏结束4.排行榜 三.游戏思路四.总结 一.前言 第一次用PyQt做游戏,有点小紧张呢。本次使用PyQt5制作一款简单的打地鼠游戏,支持基本游戏玩法、…

IO网络通信

IO Bio: 同步阻塞,一个线程一次连接,基于字符流/字节流 Nio:同步非阻塞 一个线程多个连接,多个请求注册到多路复用selector上,多路复用器轮训连接 面向缓冲区, 从某通道读取数据到缓存区&…

详解Python面向对象编程(一)

类和对象 面向过程——怎么做? (1)把完成某一需求的所有步骤、从头到尾,逐步实现 (2)根据开发需求,将某些功能独立的代码块封装成一个又一个的函数 (3)最后完成的代码&a…

csdn最全面的网安面试题汇总——小白篇

防范常见的 Web 攻击 什么是 SQL 注入攻击 攻击者在 HTTP 请求中注入恶意的 SQL 代码,服务器使用参数构建数据库 SQL 命令时,恶意SQL 被一起构造,并在数据库中执行。 用户登录,输入用户名 lianggzone,密码 ‘ or ‘…

程序员如何兼职赚小钱?

程序员由于有技术和手艺其实兼职赚钱的路子还是挺多的,只要你有足够的时间。 1. 做外包 这是比较传统的方式,甲方在一些众包平台上发布开发任务,你可以抢这个任务,但是价格都比较便宜。 任务比较多的平台: 猪八戒、一品威客、开…

【Java】哈希表

文章目录 一、概念二、哈希冲突2.1概念2.2设计合理的哈希函数-避免冲突2.3调节负载因子-避免冲突2.4闭散列-冲突解决(了解)2.5开散列/哈希桶-冲突解决(重点掌握) 三、代码实现3.1成员变量及方法的设定3.2插入3.3重新哈希3.4 获取到…

面试题-Elasticsearch集群架构和调优手段(超全面)

对于Elasticsearch(ES),我了解并有经验。在我之前的公司,我们有一个相对大型的ES集群,以下是该集群的架构和一些调优手段的概述: 1. 集群架构 集群规模:我们的ES集群由15个节点组成&#xff0c…

ICC2:postmask ECO参考脚本

更多学习内容请关注「拾陆楼」知识星球 拾陆楼知识星球入口 eco_netlist -by_verilog_file eco.v -write_changes eco.tcl set_app_options -name design.eco_freeze_silocon_mode -value true source eco tcl set_app_options -name design.eco_freeze_silocon_mode -valu…

C++ 3.25作业

1、定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream>using namespace std;namesp…

vite+vue3动态模块化导入并使用pinia

一、安装引入pinia 1.安装 pnpm install pinia # 或者使用 yarn yarn add pinia # 或者使用 npm npm install pinia 2.在main.js里引入 import { createApp } from vue import App from ./App.vue import { createPinia } from pinia createApp(App).use(createPinia()).mo…

【linux网络(一)】初识网络, 理解四层网络模型

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. 初识网络…

Python 全栈体系【四阶】(十八)

第五章 深度学习 一、基本理论 4. 神经网络的改进 4.1 神经网络的局限 全连接神经网络的局限&#xff08;一&#xff09; 未考虑数据的“形状”&#xff0c;会破坏数据空间结构。例如&#xff0c;输入数据是图像时&#xff0c;图像通常是高长通道方向上的 3 维形状。但是&a…

JavaParser 手动安装和配置

目录 前言 一、安装 Maven 工具 1.1 Maven 软件的下载 1.2 Maven 软件的安装 1.3 Maven 环境变量配置 1.4 通过命令检查 Maven 版本 二、配置 Maven 仓库 2.1 修改仓库目录 2.2 添加国内镜像 三、从 Github 下载 JavaParser 3.1 下载并解压 JavaParser 3.2 从路径打…