JVM (simple Version)

news2024/11/23 3:15:02

简介

JVM 其实就是一个Java进程 , 从操作系统申请一大块内存区域, 供 java 代码使用 .

申请出的内存 , 进一步划分 , 给出不同的用途 .

JVM 内存区域划分 :

堆中存放就是 new 出来的对象. (成员变量)

栈 是用来维护方法之间的调用关系 (局部变量)

元数据区(或者叫方法区) 存放的是类加载之后的类对象 与  静态变量 

虚拟机栈 是给 java 代码使用的, 本地方法栈 是给 jvm 内部的本地方法(C++代码)使用的 .

可以认为虚拟机栈 中包含了很多个元素, 每一个元素表示一个方法 , 每个元素称为是一个栈帧 , 每一个栈帧会包含这个方法的 入口地址 , 方法参数是啥 , 放回地址是啥, 局部变量....等

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

程序计数器 用来记录当前线程执行到那个指令了.

由于 函数 调用, 也有了"后进先出" 的特点 .

数据结构的栈, 是一个通用的概念, 更广泛的概念, 此处的栈, 特指 JVM 上的一块内存空间 .

JVM 类加载机制

类加载就是 .class文件 从文件(硬盘) 被加载到内存中(元数据区)这样一个过程. 

经历这么一个过程目的是要得到 类对象 .

主要分为如下 5 步骤 :

1 . 加载 (把 .class 文件找到,打开文件,将文件内容读到内存中)

2 . 验证 (检查 .class 文件格式对不对, 在官方提供的 JVM 虚拟机规范文档上详细描述了 .class 文件的格式)

3 . 准备 (给类对象分配空间)

4 . 解析 (针对 字符串 常量进行初始化) (常量池内的符号引用替换为直接引用的过程)

              字符串常量在 .class 文件中就已经存在, 但它们只是知道彼此之间的相对位置(偏移量) , 不知道自己在内存中的实际地址 .这时候的字符串常量就是 符号引用 . 直到真正加载到内存中 , 就会把字符串常量填充到内存中的特定位置上 , 但 字符串常量之间的相对位置还是一样的 , 这时字符串有了自己真正的内存地址 .

5 . 初始化 (针对类对象进行初始化 静态成员 , 执行静态代码块.......)

类加载 采用的策略是 "懒加载" , 非必要 , 不加载

触发类加载的情况有 : 1. 创建了这个类的实例 . 2. 使用了这个类的静态方法 / 静态属性

3 . 使用子类, 会触发父类的加载 .

 双亲委派模型

双亲委派模型 描述的是 : 在 加载的时候 找 .class 文件的这个过程 .

在 JVM 中内置了 3个 类加载器 ,  目的就是为了加载类.

BootStrap ClassLoader 负责加载 Java 标准库中的类.

Extension ClassLoader 负责加载一些非标准的但是 Sun / Oracle 扩展的库的类.

Application ClassLoader 负责加载项目中自己写的类 以及 第三方库 中的类 .

首先加载一个类的时候 , 是先从Application ClassLoader 开始 , 但 Application ClassLoader 会把加载任务交给父亲 , 让父亲去进行 , 当交给 Extension ClassLoader时, 它也交给了 它的父亲 BootstrapClassLoader , 交给BootstrapClassLoader 后 , 它没有父亲 , 于是自己进行加载 , 此时 BootstrapClassLoader 就会搜索自己负责的标准库目录的相关的类 , 如果找到,就加载 , 如果没有找到, 就继续有子类加载器进行加载 . 

如果BootstrapClassLoader 没有加载 , 则ExtensionClassLoader 真正搜索扩展库相关的目录,如果找到就加载, 如果没找到, 就由子类加载器进行加载.

如果ExtensionClassLoader没有加载 , 则 Application ClassLoader 真正搜索用户项目相关的目录 , 如果找到就加载, 没找到,由子类加载器进行加载.

当Application ClassLoader 也没有加载 , 那么就会抛出异常.

垃圾回收机制 GC

垃圾指的就是 不再使用的内存 .

垃圾回收:  就是把不用的垃圾帮我们自动释放了.

GC 的好处 : 非常省心 , 让程序猿写代码简单点 , 不容易出错 .

GC 的坏处 : 需要消耗额外的系统资源 , 也有额外的性能开销 .

另外 GC还有一个关键的问题 : STW (stop the world) .

STW : 如果有时候 , 内存中的垃圾已经很多了 , 此时触发一次 GC 操作 , 开销可能非常大 , 大到把系统资源吃了大半 , 另一方法GC回收垃圾的时候可能会涉及到一些锁操作 , 导致业务代码无法正常执行 , 这样的卡顿, 极端情况下, 可能是出现几十毫秒 甚至 上百毫秒 .

但 从Java 13 开始引入了 zgc 这个垃圾回收器 , 能使 STW 控制在 1ms 以下.

GC 主要是针对堆 进行释放的 

栈 : 栈帧随着方法的调用申请, 随着方法的结束而释放 , 不需要使用到 GC

程序计数器 : 随着线程销毁,不需要用到GC.

元数据区/方法区 : 存的是类对象 , 很少会 ' 卸载 '.

GC 是以 对象 为单位进行释放的 .

GC 的工作分为两个阶段 : 

1 . 找到垃圾  (也就是判定垃圾)

2 . 再进行对象的释放 .

找到垃圾

在 Java 中 , 使用对象 , 只有通过引用来使用 !
如果一个对象 , 有引用指向它 , 则就有可能被使用到 .
 不能进行释放.                                                如果一个对象 , 没有引用指向它 , 则认为不会再被使用了 .

1) 引用计数 [ java 没有使用]

给每个对象分配一个 计数器, 每次创建一个引用指向该对象, 计数器就+1. 每次该引用被销毁了 , 计数器就 -1.

缺点 :

1) 内存空间浪费的多 (利用率低) .

如果每个对象都要分配一个 计数器 , 按照4 个字节算,如果对象多了, 占用的额外空间就会很多 .

2) 存在循环引用的问题 

但是 , 如果 a 和 b 引用 销毁 , 此时 1 号 对象和 2号 对象引用计数 都 -1, 但是结果都还是 1, 不是 0 , 因此不能释放内存 , 但是这两个对象已经没有办法被访问到了.  

2) 可达性分析 (java 的做法)

可达性分析 : 把所有这些对象被组织的结构视为是树 , 从根节点出发 , 遍历树 , 所有能被访问到的对象 , 标记为 "可达" , 不能访问到的就是不可达 .不可达的就可以作为垃圾进行回收了.

可达性分析需要进行类似于 "树遍历" 这个操作相比于引用计数来说 . 会慢一点 , 但是可达性操作并不需要一直执行 , 只需要每隔一段时间执行一次就行了.

可达性分析遍历的起点 , 称为 GCroots . (栈上的局部变量,常量池中的对象,静态成员变量) , 因此代码中有很多这样的起点 , 把每个起点都往下遍历一遍 , 就完成了一次扫描过程 .

清理垃圾

1 . 标记清除

缺点 : 内存碎片问题 , 被释放的空间是零散的 , 不是连续的 , 申请内存要求是连续空间 . 

2 . 复制算法

将内存分为两半 .

 假设 2 4 6 是垃圾, 将 1 3 5 复制到另一半, 然后将左边的空间全部清除.

每次触发复制算法 , 都是向另外一侧进行赋值 , 内存中的数据拷贝过去 .

缺点

1) 空间利用率低

2) 如果要是垃圾少 , 有效的对象多 , 复制成本就比较大了 . 

3 . 标记整理

解决了复制算法的缺点 , 类似于 顺序表删除中间元素 , 会有元素搬运的操作.

假设 要删除 1 2 4 , 得到如下 :

虽然说保证了空间利用率 , 也解决了内存碎片化问题 , 但是 效率不高 , 如果搬运的空间比较大, 此时开销也很大 . 

分代回收

上述的 3 个 方法都不完美 , 都有明显的缺点 .

因此基于上述的 3 中方法 , 搞了一个复合策略 "分代回收".

基于一个经验规律 : 如果一个东西, 存在的时间比较长了 , 那么大概率还会继续的长时间的持续存在下去 .

给对象引入一个概念 : 年龄 (这里的年龄指的是 熬过 GC 的轮次) , 年龄越大表示 存在的时间就越久 .(经过了一轮可达性分析的遍历, 发现这个对象还不是垃圾, 这就是"熬过了一轮GC")

划分为一系列区域 : 

伊甸区 : 放年纪小的对象 . 比如: 刚 new 出来的对象.

熬过一轮GC的对象就放到 幸存区 . (由伊甸区放到幸存区 使用的是复制算法) (幸存区同一时刻只能使用一个 , 在两个幸存区之间来回拷贝 , 如果这个对象已经在两个幸存区中来回拷贝很多次了, 这时候就要进入老年代了) 

老年代 : 放年纪大的对象 . (生命周期普遍更长, 因此针对老年代, 也要周期性GC 扫描, 但是频率更低了) 如果老年代的对象是垃圾了 , 使用 标记整理的方式进行释放 .

实际上 , JVM 在实现的时候, 会有一些差异 , 但会按照上述算法思想展开.有一些变化.

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

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

相关文章

联汇科技发布自主智能体 OmBot 欧姆智能体及 OmBot OS 操作系统

今年加入 OpenAI 的大牛、前特斯拉 AI 总监 Karpathy 在最近的一次开发者活动上表示:AI 智能体,代表了 AI 的一种未来! 不仅是他,全球 AI 领域的大佬和科技巨头对 AI 智能体的发展都表现出极大兴趣,并寄予厚望。 大语言…

享元模式:优化对象的共享与重复利用

享元模式是一种结构型设计模式,它通过共享对象来最大程度地减少内存使用和对象创建的开销。本文将深入探讨享元模式的原理、结构和使用方法,并通过详细的 Java 示例代码来说明。 1. 享元模式的定义 享元模式是一种通过共享对象来有效地支持大量细粒度对…

简单的聊一聊如何用CSS制作一个专业的头部页眉(Headers)

一个吸引人的网页页眉对于给访问者留下良好的第一印象至关重要。一个设计精良的页眉不仅能够吸引注意力,还能为整个网站设定基调。借助CSS,创建现代化和视觉吸引力的网页页眉比以往任何时候都更加容易。 在本文中,我们将探索一些基本的技巧和…

虹科分享 | 如何基于IO-Link wireless方案实现工厂数据采集和状态监测

数据和数字化是工业4.0变革的关键驱动因素。从整个工厂的传感器获取数据,除了优化制造计划和流程外,还能实现强大的分析和决策。目前,基于数据的应用正在催生更多智能解决方案,以提高制造业的灵活性和敏捷性,进而提高效…

【unity细节】GameObject.Find和 transform.Find 核心区别

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:unity细节和bug ⭐Find找子对象的子对象,GameObject.Find 和 transform.Find的区别 ⭐ 文章目录 …

jni编程(windows+JDK11+clion)

JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 一、java代码 package org.example;public class Main {static {System.load("");}public static void main(String[] args) {Syste…

CMU 15-445 -- SortingAggregations - 08

CMU 15-445 -- Sorting&Aggregations - 08 引言Sorting为什么需要排序AlgorithmsExternal Merge Sort2-Way External Merge SortGeneral External Merge Sort实例:Sort 108 pages file with 5 buffer pages:N 108, B 5 Using B Trees Aggregations…

JavaScript--修改 HTML 元素

这些是一些用于修改 HTML 元素的常见方法&#xff1a; 1、document.createElement(element)&#xff1a;创建 HTML 元素节点。可以使用这个方法创建一个新的 HTML 元素&#xff0c; 例如 document.createElement(div) 将创建一个 <div> 元素节点。 2、document.createA…

freemarker学习

一、Freemarker引入 二、环境搭建和测试 pom.xml <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/m…

PyTorch: nn网络层-卷积层

文章和代码已经归档至【Github仓库&#xff1a;https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 pytorch教程 也可获取。 文章目录 nn网络层-卷积层1D/2D/3D 卷积一维卷积二维卷积三维卷积 二维卷积&#xff1a;nn.Conv2d()卷积尺寸计算简化版卷积尺…

华为路由器如何通过Console口进行基本配置

华为HCIA试听课程&#xff1a;不会传输层协议&#xff0c;HCIA都考不过https://mp.weixin.qq.com/s/oKAL8GvdrcHEb5O_8bEZZQ 思科CCNA试听课程&#xff1a;适合初学者&#xff1a;VLAN原理与配置https://mp.weixin.qq.com/s/toIJg1EVFImalrwzbTONTQ 组网图形 组网需求 通过Cons…

【2023,学点儿新Java-31】测试:整型和浮点型变量的使用 | 附:计算机存储单位(转换关系)| 企业真题:为什么0.1+0.2不等于0.3

前情提要&#xff1a; 【2023&#xff0c;学点儿新Java-30】变量的基本使用&#xff1a;变量的意义 | 变量的构成要素 | Java中变量的数据类型、变量的使用 | 附&#xff1a;Java中变量的作用域 | 数据类型、变量名和变量值哪个最重要&#xff1f;【2023&#xff0c;学点儿新J…

vue中使用v-for实现两次嵌套循环,判断某子元素是否显示,进行复杂表单校验

一、需求场景&#xff1a; 有以下一个使用场景&#xff0c;名称111、名称222、名称333&#xff0c;是放在一个大数组里的&#xff0c;然后通过第一层for循环显示出来的。名称333数组里又包含自己的子数组&#xff0c;子数组再通过第二次for循环展示出来。当我们选择发放方式的…

基于Javaweb实现ATM机系统开发实战(七)用户密码修改

接下来我们完成密码修改的功能&#xff0c;还是老规矩先看前端界面&#xff1a;这里我们先把需要的变量进行修改&#xff0c;然后把卡号变成不可修改&#xff1a; <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-…

深度挖掘文物价值,VR博物馆讲好文物故事

文物不言&#xff0c;自有春秋。丝绸、字画、瓷器、古玩等&#xff0c;铺陈的是传奇&#xff0c;激荡的是灵魂。历史文物珍贵的莫过于其历史与文化的价值&#xff0c;倘若不能被更多的人欣赏、研究、传承&#xff0c;那么这些文物就很难实现“价值外溢”。 单纯的去读历史课本&…

分层解耦-三层架构

三层架构 在上篇文章的案例中写文章-CSDN创作中心 的Controller类承担了对于数据操作&#xff08;访问&#xff09;、对于对于数据的逻辑处理、以及接受请求响应数据的工作&#xff0c;对于类似的小项目来说冗杂程度还可以接收&#xff0c;但是如果项目更加复杂&#xff0c;就…

QT使用同一按钮实现打开/关闭新窗口

QT使用同一按钮实现【打开/关闭】新窗口&#xff0c;实现方案如下&#xff1a; 使用一个全局状态变量记录窗口打开状态通过该状态实现新窗口的show和close 实现代码如下&#xff1a; #include "mainwindow.h" #include "ui_mainwindow.h" #include "…

怎么用PDF24 Tools工具在线进行PDF文件合并

PDF文件是经常会被用到&#xff0c;它在我们的日常生活和工作中扮演着重要的角色。PDF文件合并是将多个PDF文件合并为单个文件&#xff0c;这个过程通常是为了方便管理多个PDF文件&#xff0c;或者将多个PDF文件合并为一个整体以便于共享或打印。既然如此&#xff0c;如何快速合…

达梦sql执行计划、HINT、索引简单应用

目录 收集统计信息. 3 1. 通过DBMS_STATS包中的方法. 3 2、删除指定表的统计信息. 3 执行计划. 3 常用执行计划操作符. 4 统计指定sql执行号的所有操作符的执行时间. 5 HINT 5 并行操作&#xff1a;. 6 查询计划重用、结果集重用. 7 示例. 8 1、收集统计信息&#x…

Comparable/Comparator

现在有一个自定义学生的学生类,里面有name属性,和age属性,我们如何去比较大小? class Student{public String name;public int age;public Student(String name, int age) {this.name name;this.age age;}Overridepublic String toString() {return "Student{" &…