[JVM]JVM内存划分, 类加载过程, 双亲委派模型,垃圾回收机制

news2024/12/24 11:38:38

文章目录

  • 一. JVM内存划分
    • 1. 堆
    • 2. 栈
    • 3. 元数据区
    • 4. 程序计数器
  • 二. 类加载过程
    • 1. 加载
    • 2. 验证
    • 3. 准备
    • 4. 解析
    • 5. 初始化
  • 三. 双亲委派模型
  • 四. JVM的垃圾回收机制GC
    • 1. 找到需要回收的对象
    • 2. 释放垃圾的策略

一. JVM内存划分

JVM就是java进程
这个进程一旦跑起来, 就会从操作系统这里, 申请一大块内存空间
JVM接下来就要进一步的对这个大的空间, 进行划分, 划分成不同区域, 从而每个与都有不同的功能作用
在这里插入图片描述

1. 堆

整个内存区域中, 最大的区域
放的是代码中new出来的对象和成员变量

2. 栈

分为JVM虚拟机栈和本地方法栈

栈, 保存了方法中的调用关系 和 局部变量
JVM虚拟机栈, 是java使用的
本地方法栈, 是给本地方法使用的

3. 元数据区

放的是类对象 Test.class 和 类中的方法 和 常量池
代码中的每个类, 在jvm上运行的时候, 都会有对应的类对象
类有一些方法, 每个方法, 都代表了一系列的"指令集合"(JVM字节码指令)

4. 程序计数器

是内存区域中最小的区域, 只需要保存当前要执行的下一条指令的地址(这个地址就是元数据区里面的一个地址)

在这里插入图片描述
上述代码:
Test类的类对象, 在元数据区
a, 是成员变量, 在堆上
t2, 是成员变量, 在堆上, 存放的是Test2实例的地址
new Test2(), 在堆上
s, 是成员变量, 在堆上, 存放指向"hello"的地址
“hello”, 是常量, 存储在常量池中, 在元数据区
b, 是静态成员变量, 成为了类属性, 在元数据区
t, 是局部变量, 在栈上, 存放的是Test实例的地址
new Test(), 在堆上
在这里插入图片描述

上四个区域, 堆 和 元数据区, 是整个进程只有一份, 每个线程共享
栈 和 程序计数器, 是每个线程都有一份, 各个线程不可共享的

二. 类加载过程

我们写的代码, 是.java文件, 存储在硬盘上
javac编译器将.java文件, 编译成.class文件
类加载的过程, 就是将.class文件加载到JVM中, 得到类对象

1. 加载

在硬盘上, 找到对应的.class文件, 读取文件内容

2. 验证

检查.class里面的内容, 是否符合要求
在这里插入图片描述
这是java虚拟机规范中的.class文件的格式
把读取的内容, 往这个格式里套, 如果能套用, 就没有问题

3. 准备

给类对象, 分配内存空间(元数据区)
把这个空间中的数据都填充成0

4. 解析

针对字符串常量初始化
把刚才的.class文件中的常量内容, 取出来, 放到"元数据区"

5. 初始化

针对类对象 静态成员进行初始化, 执行静态代码块
(不是针对对象初始化, 和构造方法无关)

此时, 类对象就搞定了

三. 双亲委派模型

双亲委派模型, 是类加载中的第一步, 找.class文件的过程, 根据"全限定类名"找到对应的.class文件
“全限定类名”, 就是报名 + 类名 :String => java.lang.String
“类加载器”, 可以当做JVM中包含的一个特定的模块, 这个模块就是负责类加载的工作
JVM内置了三个类加载器:

  1. BootstrapClassLoader
    负责加载标准库中的类(爷爷)
  2. ExtentionClassLoader
    负责加载JVM扩展库的类(父亲)
  3. ApplicationClassLoader
    负责加载第三方库的类, 和你自己写的代码的类(儿子)

这个的父子关系,是通过类加载器中存在一个parent这样的字段, 指向自己父亲

工作过程:
例如找我自己写的类: java.Test
在这里插入图片描述
简单一句话概括, 就是拿到任务, 先交给父亲处理, 父亲处理不了, 再自己处理

上述过程, 主要为了应对这个场景:
比如自己代码里写了一个类, 类的名字和标准库/扩展库冲突了, JVM会确保加载的类是标准库的类, 就不加载你自己写的类了

四. JVM的垃圾回收机制GC

1)程序计数器, 不需要额外回收, 线程销毁, 自然回收
2)栈, 不需要额外回收, 线程销毁, 自然回收
3)元数据区, 存的是类对象, 一般不需要回收
所以, GC回收的是内存, 更准确说, 是对象, 回收的是"堆"上的内存

GC的流程:

  1. 找到谁是垃圾
  2. 释放对应的内存

1. 找到需要回收的对象

一个对象, 什么时候创建, 时机往往是明确的, 但是什么时候不再使用, 实际往往是模糊的
在编程中, 要确保, 代码中使用的对象, 都是有效的, 千万不要出现"提前释放"的情况

所以, 我们判断是否回收的保守办法, 就是判定某个对象, 是否存在引用指向他
在这里插入图片描述
在这里插入图片描述
具体怎么判定某个对象, 是否有引用指向?
下面介绍两种方式
** 1. 引用计数**
注意: 这个方法不是JVM采用的方案, 而是Python/PHP的方案
在这里插入图片描述
这种方法存在两个缺陷:
1.消耗额外的存储空间
如果对象比较大, 浪费的空间还好, 如果对象比较小, 空间占用就多了
2.存在**“循环引用”**问题
在这里插入图片描述
在内存中是这样的:
在这里插入图片描述

现在有下面代码:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

此时, 俩对象相互指对方, 导致两个对象的引用计数, 都不为1, (不为0, 就不是垃圾), 但是外部的代码, 也无法访问到这俩对象, 出现"循环引用"

2. 可达性分析
注意: 可达性分析是JVM采取的方案
JVM把对象之间的引用关系, 理解成了一个**“树形结构”**
JVM就会不停地周期性遍历这样的结构, 把所有能够遍历访问到的对象标记成"可达", 剩下的就是"不可达"
假设对象之间存在这样的树形引用关系:
在这里插入图片描述
此时, 从a出发, 任何一个结点都是可达的, 没有垃圾
如果此时c中的引用f = null, 相当于c.right = null, 那么f 就不可达了, 此时后续进行遍历时, 就会把f标记成垃圾了
如果此时a中c的引用c = null, 相当于a.right = null, 那么c和f都不可达了, 此时后续遍历, 就会把c和f标记成垃圾

由于可达性分析, 需要消耗一定的时间, 因此java垃圾回收, 没法做到"实时性", JVM提供了一组专门负责GC的线程, 不停地进行扫描工作

2. 释放垃圾的策略

1. 标记清除
直接把标记为垃圾的对象对应的内存, 释放掉(简单粗暴)
在这里插入图片描述将灰色的回收掉
这样的做法, 就会存在"内存碎片", 空闲内存被分成一个个小的碎片了, 后续很难申请到大的内存
申请内存, 都是要申请"连续"的内存空间的

2. 复制算法
在这里插入图片描述
比如, 要释放246, 保留135, 不会直接释放246, 而是先把135拷贝到另一块连续的内存上去

在这里插入图片描述
虽然内存碎片问题解决了, 但是空间浪费太多了
3. 标记整理
在这里插入图片描述
采用类似"顺序表删除中间元素"的方法, 向前搬运
先回收2, 把3向前搬运, 再释放4, 5向前搬运…
但是, 这样的搬运, 时间开销很大

其实, JVM实际的方案, 是综合上述的方案, 分代回收
根据对象的’‘年龄’'选择不同的方案
某个对象, 经历了一轮GC之后, 还是存在, 那么GC + 1

实际上堆区是分成Young区和Old区
在这里插入图片描述
Eden: 伊甸区
s0,s1: 生存区/幸存区

把创建的对象, 放在伊甸区中, 伊甸区大部分的对象, 生命周期都是比较短的, 第一轮GC到达的时候, 就会成为垃圾
把第一轮剩下的对象, 通过复制算法, 复制到生存区
每经过一轮GC, 生存区中都会淘汰掉一批对象, 剩下的通过复制算法, 进入到另一个生存区, 同时, 伊甸区中生存下来的也复制到这个生存区中
某些对象, 经历了很多轮GC, 都没有称为垃圾, 就会复制到Old区
Old区的对象, 也是需要GC的, 但是老年代的对象生命周期比较长, 就可以降低GC的扫描频率
对象在Old区, 就通过标记整理方法来进行回收

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

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

相关文章

Windows本地制作java证书(与jeecgboot配置本地证书ssl问题)

1:JDK生成自签证书SSL,首先以管理员身份运行CMD窗口,执行命令 keytool -genkey -alias testhttps -keyalg RSA -keysize 2048 -validity 36500 -keystore "F:/ssl/testhttps.keystore"F:\ssl>keytool -genkey -alias testhttps -keyalg R…

PCIe进阶之TL:Memory, I/O, and Configuration Request Rules TPH Rules

1 Memory, I/O, and Configuration Request Rules 下述规则适用于 Memory 请求、IO 请求和配置请求。 除了公共的 header 字段外,所有 Memory 请求、IO 请求和配置请求还包括以下字段: (1)Requester ID[15:0] 和 Tag[9:0],组成了 Transaction ID 。 (2)Last DW BE[3:0]…

计算架构模式之接口高可用

接口高可用整体框架 接口高可用主要应对两类问题:雪崩效应和链式效应。 雪崩:当请求量超过系统处理能力之后,会导致系统性能螺旋快速下降,本来系统可以处理1000条,但是当请求量超过1200的时候,此时性能会下…

【415】【最高乘法得分】

目录 使用dp python版本 java版本 递推式 python版本 java版本 PS: java语法 1.定义数组 2.记忆化 3.计算max 难绷,本来想着4个指针,和四数之和那道题挺类似的。。。。 四数之和好像剪枝和预处理都是先排序的比较好做。 无奈,只…

[网络]https的概念及加密过程

文章目录 一. HTTPS二. https加密过程 一. HTTPS https本质上就是http的基础上增加了一个加密层, 抛开加密之后, 剩下的就是个http是一样的 s > SSL HTTPS HTTP SSL 这个过程, 涉及到密码学的几个核心概念 明文 要传输的真正意思是啥 2)密文 加密之后得到的数据 这个密文…

CTF(misc)1和0的故事

题目链接 下载题目后是一堆整齐的01字符串,猜测是生成二维码,将0变成白色方块,1变成黑色方块。 0000000001110010000000000 0000000000011110100000000 0000000001110001000000000 0000000010111100000000000 0000000010101010000000000 00…

Python基础语法(3)下

列表和元组 列表是什么,元组是什么 编程中,经常需要使用变量,来保存/表示数据。变量就是内存空间,用来表示或者存储数据。 如果代码中需要表示的数据个数比较少,我们直接创建多个变量即可。 num1 10 num2 20 num3…

ModuleNotFoundError: No module named ‘datasets‘

报错信息: 解决:安装datasets 方法1: pip install datasets 方法2: python3可以使用以下命令: pip3 install datasets

【智路】智路OS Perception Fusion Service

Perception Fusion Service https://gitee.com/ZhiluCommunity/airos-edge/raw/r2.0/docs/02_Service/Perception_Fusion_Service.md 多传感器融合感知模块的主要任务是接收各传感器感知的障碍物信息,融合这些障碍物信息,得到融合后的障碍物信息。 智…

Tuxera NTFS for Mac 2023绿色版

​ 在数字化时代,数据的存储和传输变得至关重要。Mac用户经常需要在Windows NTFS格式的移动硬盘上进行读写操作,然而,由于MacOS系统默认不支持NTFS的写操作,这就需要我们寻找一款高效的读写软件。Tuxera NTFS for Mac 2023便是其中…

接口自动化框架入门(requests+pytest)

一、接口自动化概述 二、数据库概述 2.1 概念 存储数据的仓库,程序中数据的载体 2.2 分类 关系型数据库:安全 如mysql,oracle,SQLLite database tables 行列 非关系型数据库:高效 如redis,mongoDB 数…

C++笔记之子类初始化时父类带参构造函数的处理、父子类中模板参数的传递

C++笔记之子类初始化时父类带参构造函数的处理、父子类中模板参数的传递 code review! 文章目录 C++笔记之子类初始化时父类带参构造函数的处理、父子类中模板参数的传递一.子类初始化时父类带参构造函数的处理1.1.若父类只有带参数的构造函数,子类初始化时必须在初始化列表…

C++ 面试必备知识大全:从基础到高级特性全面解析

创作不易,您的打赏、关注、点赞、收藏和转发是我坚持下去的动力! C 面试中常见的问题涵盖了语言基础、面向对象编程、内存管理、STL(标准模板库)、并发编程、设计模式等。以下是一些常见的 C 面试问题及其详细答案总结&#xff1…

protobuf中c、c++、python使用

文章目录 protobuf实例:例题1:[CISCN 2023 初赛]StrangeTalkBot分析:思路:利用: 例题2:[CISCN 2024]protoverflow分析: protobuf Protocol Buffers,是Google公司开发的一种数据描述语…

Tcl lnit error: Can’t find a usable init.tcl in the following directories 问题解决

这个问题出现在我用py2exe打包了一个包含tkinter的图形化界面,在当前电脑上运行无问题,在移动到新电脑上后提示报错、 这里吐槽一下,新电脑上报错信息一闪而过,我用的土法子解决的,就是录视频然后0.25倍速度暂定找到报…

删除Cookie原理

WebServlet("/deletecookie") // 这个注解指定了Servlet的URL映射路径 public class DeleteCookieServlet extends HttpServlet { // 定义一个继承自HttpServlet的类Override // 重写父类的方法protected void doGet(HttpServletRequest request, HttpServletResp…

ORM框架详解:为什么不直接写SQL?

想象一下,你正在开发一个小型的在线书店应用。你需要存储书籍信息、用户数据和订单记录。作为一个初学者,你可能会想:“我已经学会了SQL,为什么还要使用ORM框架呢?直接写SQL语句不是更简单、更直接吗?” 如…

【CS110L】Rust语言 Lecture3-4 笔记

文章目录 第三讲 所有权:移动与借用&例1例2例3 错误处理(开头)为什么空指针如此危险,我们能做什么以应对?— 引出Optionis_none()函数unwrap_or()函数常见用法 第四讲 代码实践:链表Box节点和链表的定义节点和链表的构造函数判…

基于Sobel算法的边缘检测设计与实现

1、边缘检测 针对的时灰度图像,顾名思义,检测图像的边缘,是针对图像像素点的一种计算,目的时标识数字图像中灰度变化明显的点,图像的边缘检测,在保留了图像的重要结构信息的同时,剔除了可以认为…

【WPF】桌面程序开发之xaml页面绑定数据模型详解

使用Visual Studio开发工具,我们可以编写在Windows系统上运行的桌面应用程序。其中,WPF(Windows Presentation Foundation)项目是一种常见的选择。然而,对于初学者来说,WPF项目中xaml页面的布局设计可能是一…