JVM(Java Virtual Machine) 详解

news2024/11/29 2:36:00

1. JVM 内存区域划分

一个 Java 写的程序,跑起来就得到了一个 Java 进程(资源分配的基本单位)= JVM + 上面运行的字节码指令

1) 程序计数器(比较小的空间),保存了下一条要执行的指令的地址

这个不是 CPU 的寄存器,而是内存空间;这里的 “下一条要执行的指令” 是 Java 的字节码(不是 cpu 的 二进制 的机器语言)

2) 堆

JVM 上最大的空间,new 出来的对象都在 堆 上

3) 栈(Stack Overflow)

函数中的局部变量,函数的形参,函数之间的调用关系

Java 虚拟机栈(JVM 之上,运行的 Java 代码的方法调试关系)

本地方法栈(JVM 里,C++ 代码的函数调用关系)

4) 元数据区(方法区)

Java 程序中的指令(指令都是包含在类的方法中)

保存了代码中涉及到的 类的相关信息

类的 static 属性

分析:

在一个 Java 进程中,元数据区和堆都是只有一份的(同一个进程中的所有线程都是共用同一份数据的,共用同一份内存空间)

程序计数器和栈,则可能有多份(当一个 Java 进程中有多个线程的时候,每个线程都有自己的程序计数器和栈)

线程就代表一个 “执行流”,每个线程就需要保存自己的 “程序计数器”,每个线程也需要记录自己的调用关系

例:以下代码中创建的变量处于哪个内存区域

class test {
    private int a;
    private Test b = new Test();
    private static int c;
    private static Test d = new Test();

    public static void main(String[] args) {
        int e = 10;
        Test f = new Test();
    }
}

tip:一个变量处于哪个内存区域和变量是不是 “内置类型” 无关,而是和变量的形态有关

1) 局部变量 -> 栈

2) 成员变量 -> 堆

3) 静态成员变量 -> 元数据区(方法区)

2. JVM 类加载的过程

2.1 概述

加载一个 .class 文件就会创建一个对应的类对象

基于该类对象就可以创建该类的实例,类对象其实就是“对象”的说明书/蓝本

类对象例就包含了 .class 文件中的各种信息,如:

类的名字

类里有哪些属性,每个属性名字,每个属性的类型,public/private

类里有哪些方法,每个方法的名字、参数,public/private

继承的父类是啥

实现的接口有哪些

...

2.2 类加载的具体步骤(重要

五个环节 / 三个环节(将中间3个环节合为1个)

1. 加载

把 .class 文件找到,代码中先找到类的名字,再找到对应的 .class 文件(涉及到一系列目录查找的过程),打开并读取文件内容

2. 验证

验证读到的 .class 文件中的数据是否正确合法(Java 标准文档中,明确定义了 .class 文件的格式)

Java 标准文档

3. 准备(分配内存空间)

最终需要得到类对象 => 需要内存

把刚才读取到的内容,确定出类对象需要的内存空间,申请这样的内存空间,并把内存空间中所有的内容都初始化为 0(Java 操作,C/C++ 申请到的内存不会进行置 0 操作)

置 0 操作是为了避免上次残留的数据被当前程序误使用

4. 解析(主要针对类中的字符串常量进行处理)

解析阶段是 Java 虚拟机将常量池内的 “符号引用(字符串常量,已经在 .class 文件中)” 替换为 “直接引用(里面保存了变量的地址)” 的过程,也就是初始化常量的过程

5. 初始化(针对类对象做最终的初始化操作)

执行静态成员的赋值语句

执行类中的静态代码块

针对 父类 也要加载

3. 面试题:双亲委派模型

3.1 概念

是类加载五个步骤中,第一个步骤里面的一个环节:给定 类全限定名,找到 对应的 class 文件位置

类加载器 JVM 中,已经内置了一些类加载器(JVM 中的功能模块),完成上述的 “类加载” 过程

JVM 默认有三个类加载器

(爷爷)BoorsrtapClassLoader 负责加载标准库中的类(标准库的类,也是有一个专门存放位置的)

(爸爸)ExtensionClassLoader 负责加载扩展类(JVM 厂商对 Java 功能做出的一些扩展)

(儿子)ApplicationClassLoader 负责加载第三方库中的类 / 自己写的代码中的类

tip:上面三者不是 Java 中父类子类的继承关系,而是在类加载器中有一个 parent 这样的引用指向父亲

3.2 双亲委派模型的工作流程

输入:类的全限定名(字符串),类似于 java.lang.String

输出:找到对应的 .class 文件

这样设定的最核心目的:防止用户自己写的类把标准库的类给覆盖掉

保证标准库的类被加载的优先级最高(扩展库其次,最后是第三方库)

4. 垃圾回收机制(GC)

4.1 引子

C/C++ 中,malloc/new 一个对象后,都需要手动释放内存 free/delete,如果不释放就可能产生内存泄漏(头号职业杀手,后果可能很严重,排查非常不好搞)

C 中,针对内存泄漏,直接摆烂了

C++ 中,针对内存泄漏,给出了一个方案:“智能指针”(然而它并不太智能)

在 Java 中对内存泄漏给出更系统更完整的解决方案(在 Java 之前也有一些语言使用了这样的方案)

垃圾回收,有了它,程序员可以放心大胆的 new,JVM 会自动识别哪些 new 完的对象再也不用了,就会把这样的对象自动释放掉

其他语言一看都觉得香,纷纷投入到 垃圾回收 的怀抱,如:Go、Python、PHP、JS...主流语言中大部分都是内置了 GC

但是,GC 也是有代价的,C++ 不是搞不了 GC,而是开发 C++ 的大佬们评估了之后,不愿承担这些代价

GC 需要 JVM 中引入额外的逻辑:

1) 消耗不少 CPU 开销,进行垃圾的扫描和释放

2) 进行 GC 的时候可能会触发 STW(Stop The World)问题,导致程序卡顿,对于性能要求高的场景就会影响很大

因此,GC 就会 提高开发效率,影响运行效率

4.2 Java 的垃圾回收

JVM 中有好几个内存区域,GC 主要负责的是 堆

其中 程序计数器和栈 是跟随线程的;元数据区(一个程序里面要加载的类都是有上限的,不会出现无限增长的情况)

垃圾回收,也就是回收内存,是以对象为维度进行回收的(回收对象)

4.3 GC 具体怎样回收

1) 先找出谁是垃圾

需要针对每个对象分别判定,是否为垃圾

在 Java 中使用一个对象,一般都是通过 “引用” 来使用的,如果一个对象没有引用指向了,就可以认为这个对象是垃圾了

方案一:引用计数

给每个对象分配一个计数器,衡量有多少个引用指向

每增加一个引用,计数器 + 1

每减少一个引用,计数器 - 1

当计数器减为 0,此时对象就是垃圾了

这样做是可以回收垃圾,但是假设 Test 类就只有一个 int 成员(4字节),此时为了引入引用计数,少说得搞个 short(2字节),内存多占用了 50%

上述引用计数方案在 Java 中(JVM)没有采纳,因为其存在两个问题:

1) 消耗额外的空间

2) 引用计数可能导致 “循环引用”,使得上述的判定出错(和死锁类似),如下示例:

以上的循环引用也是有解的,但是需要引入更多机制(环路检测),代价就更大了


方案二:可达性分析(用时间换空间)

JVM 中专门搞了一些线程,周期性的扫描代码中的所有对象,判定某个对象是否是 “可达”(可以被访问到),对应的,不可达的对象就是垃圾了

如上图,JVM 中进行可达性分析的线程就是在做这样的事,从 root 出发,尽可能的通过 root 访问到更多的对象,相当于遍历的过程(严格来说,不是 “树的遍历”,而是 “图的遍历”)

可达性分析的起点称为 GC root,一个程序中 GC root 不是只有一个,而是有很多个,例如:

1) 栈上的局部变量(引用类型)

2) 方法区中,静态的成员(引用类型)

3) 常量池引用指向的对象

把所有的 GC root 都遍历一遍,针对每一个都尽可能往下延申...

可达性分析是很消耗时间的,尤其是当程序中对象特别多的情况下


2) 释放垃圾的内存空间

a) 标记-清除(直接针对内存中的对应对象进行释放)

当标记到垃圾位置,并对其清除时,会引入 “内存碎片问题”,哪些对象要释放是随机的,很可能要释放的位置对于整个内存来说不是连续的,虽然将上述内存释放掉了,但是整体这些空闲内存并没有连在一起,后续申请内存的时候,就可能申请不了(申请的内存一定是连续的)

例如:上述回收完垃圾后,空闲内存一共 3M,现要申请 2M 的空间时,就会失败(不考虑未分配空间)

因此,尽量避免内存碎片,是释放内存的关键问题


b)  复制算法

将内存一分为二,同一时刻只使用其中的一半

当要回收垃圾时,将不是垃圾的对象拷贝到另一侧(确保这些被拷贝的对象是连续的)

然后将要回收一侧的空间全部释放掉

虽然能解决 “内存碎片化” 的问题,但是其缺点也很明显:

1) 内存空间利用率低

2) 如果存活下来的对象比较多,复制成本也比较大


c) 标记-整理

非常类似于顺序表中删除中间的元素

缺点:这样搬运的开销依旧不小


d) 分代回收

JVM 中真正的解决方案是把上面几个方案综合一下,取长补短

JVM 根据对象的年龄,把对象区分成:新生代(年轻的)、老年代(年老的)

tip:可达性分析是周期性的,每经过一轮扫描,对象仍然存活(不是垃圾),年龄 + 1

根据经验规律:

1. 绝大部分的新对象活不过第一轮 GC,留存下来的对象拷贝到幸存区

2. 幸存区是两个相等的空间,也是按照复制算法反复进行多次

新生代中,真正要拷贝的对象不多(经验规律),内存利用率低

3. 如果一个对象在幸存区已经反复拷贝多次,不是垃圾,年龄不断增长,达到一定程度之后,对象就要拷贝到老年代了

4. 根据经验规律,老年代中的对象生命周期都比较长,老年代的对象肯定还会进行可达性分析,但是进行 GC 的频率就会降低

另外,老年代也是通过标记整理的(需要整理的次数不多)

4.4 JVM 中的垃圾回收器

分代回收是 JVM 中 GC 的基本思想方法,具体落实到 JVM 的实现层面上,JVM 还提供了多种 “垃圾回收器” 对上述的分代回收做进一步的扩充和具体实现

1. CMS GC

CMS 设计理念:把整个 GC 过程拆分成多个阶段,能和业务线程并发运行的,就尽量并发,尽可能减少 STW 的时间

2. G1 GC

G1 是把内存分成很多块,不同的颜色(字母)表示这一小块是新生代(伊甸区/幸存区)、老年代

进行 GC 的时候,不要求一个周期就把所有的内存都回收一遍,而是一轮 GC 只回收其中的一部分就好(限制一轮 GC 花的时间/工作量),使 STW 的时间在可控范围之内

这些方案的主要目的:降低 STW 的影响(ZGC,目前比较新的垃圾回收器),目前可以把 Java STW 的时间降低到 1ms 以内

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

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

相关文章

API接口开发系列文章:构建高效、安全与可扩展的API服务

前言 在当今的数字化时代,API(应用程序编程接口)已成为连接不同系统、服务和应用的核心桥梁。无论是企业内部的数据交互,还是面向第三方的服务开放,API都扮演着至关重要的角色。本系列文章将深入探讨API接口开发的各个…

两名大学生利用Meta的智能眼镜展示了一项令人震惊的技术,能够实时“人肉”他人的身份信息

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

每日一练:地下城游戏

174. 地下城游戏 - 力扣(LeetCode) 题目要求: 恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔…

第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第二套)

一.题目分析 (1).题目 (2).题目分析 1.按键功能分析 a.B1界面切换 b.B2每次按下R加2 c.B3每次按下R减2 d.LED控制按键 2.信号输出功能分析 a.PA7信号输出-----信号的输出就需要使用到输入捕获和输出比较功能 b.输出信号的…

云原生(四十四) | 远程连接ECS服务器

文章目录 远程连接ECS服务器 一、自带连接工具连接ECS云服务器 二、为什么要使用远程连接工具 三、远程连接ECS服务器四要素 1、用户名 密码 2、IP地址(公网IP) 3、SSH端口号 4、阿里云安全组 四、使用MobaXterm远程连接ECS云服务器 五、ECS云…

前端练习小项目 —— 让图片变得更 “色”

前言:相信读者在学习完了HTML、CSS和JavaScript之后已经想要迫不及待的想找一个小型的项目来练练手,那么这篇文章就正好能满足你的 “需求”。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 在开始学习…

【隐私计算篇】多方安全计算之函数秘密共享(FSS)

1. 函数秘密共享(FSS)定义 秘密共享是一种将一个值拆分为多个份额的方法,形式有多种,可以参考《安全多方计算(MPC)矩阵乘法算子的原理分析》。这里主要提及加法秘密共享,使得:这些份额可以重新组合以还原出秘密值;任…

HTMLCSS练习

1) 效果如下 2) 代码如下 2.1) HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conte…

IPS和IDS有啥区别

在网络安全领域&#xff0c;入侵检测系统 (IDS) 和入侵防御系统 (IPS) 是两种关键的技术&#xff0c;旨在保护网络免受各种威胁。这两者尽管名字相似&#xff0c;但在功能、配置、以及应用场景等方面都有着显著的差异。 入侵检测系统 (IDS) IDS 是一种被动监控系统&#xff0c…

自建动态IP代理为何无法使用及解决方法

在网络使用中&#xff0c;有时候我们自建动态IP代理来实现一些特定的需求&#xff0c;例如访问受限内容或保护隐私。然而&#xff0c;有时我们会遇到无法使用的情况。本文将探讨无法使用的可能原因&#xff0c;并提供相应的解决方法。 1. 可能原因 a. 网络配置问题 自建动态I…

Chromium 关闭 Google Chrome 后继续运行后台应用功能分析c++

此功能允许关闭 Google Chrome 后继续运行后台&#xff0c;控制此功能的开关是 // Set to true if background mode is enabled on this browser. //更改此值可以修改默认开启关闭 inline constexpr char kBackgroundModeEnabled[] "background_mode.enabled"; …

Python爬虫(五)--爬虫库的使用(Python Crawler (5) - Use of Crawler Libraries)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

如何解决在 nextjs 中使用 sequelize 连接 mysql 报错:Please install mysql2 package manually

解决方案 手动设置 dialectModule 的值为 mysql2。增加 dialectModule 配置即可。 import mysql2 from mysql2 import { Sequelize } from sequelizeconst { DB_DATABASE, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_LOGGING, DB_POLL_MAX, DB_POLL_MIN, DB_POLL_ACQUIRE, …

Android Framework(八)WMS-窗口动效概述

文章目录 动画简述本地、远端动画的定义什么是“leash”图层“leash”图层的命令与创建 Winscope流程小结 动画流程概览分析Activity启动app_transition 动画的主要事件触发动画执行的套路动画真正执行动画的结束回调触发远端动画的Target 动画简述 1、动画的原理也是利用了视觉…

vue3 + Ant design vue formItem 无法使用嵌套的form表单校验

文章目录 前言一、背景在这里插入代码片二、操作步骤1.复现前的准备工作&#xff08;1&#xff09;vue版本和ant design vue 版本&#xff08;2&#xff09;任意页面的代码 2.解决问题3.自定义表单校验的代码 总结 前言 提示&#xff1a; 一、背景在这里插入代码片 背景&…

音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现

一、SCRIPTDATAVALUE类型 从《音视频入门基础&#xff1a;FLV专题&#xff08;9&#xff09;——Script Tag简介》中可以知道&#xff0c;根据《video_file_format_spec_v10_1.pdf》第80到81页&#xff0c;SCRIPTDATAVALUE类型由一个8位&#xff08;1字节&#xff09;的Type和…

go语言protoc的详细用法与例子

一. 原来的项目结构 二. 选择源proto文件及其目录&目的proto文件及其目录 在E:\code\go_test\simple_demo\api 文件夹下&#xff0c;递归创建\snapshot\helloworld\v1\ad.pb.go E:\code\go_test\simple_demo> protoc --go_outpathssource_relative:./api .\snapshot\h…

数据结构--二叉树的顺序实现(堆实现)

引言 在计算机科学中&#xff0c;二叉树是一种重要的数据结构&#xff0c;广泛应用于各种算法和程序设计中。本文将探讨二叉树的顺序实现&#xff0c;特别是堆的实现方式。 一、树 1.1树的概念与结构 树是⼀种⾮线性的数据结构&#xff0c;它是由 n(n>0) 个有限结点组成…

新款平行进口奔驰GLS450升级原厂AR实景导航人机交互行车记录仪等功能

平行进口的24款奔驰GLS450升级原厂中规导航主机通常具备以下功能&#xff1a; 人机交互系统&#xff1a;该导航主机配备了人机交互系统&#xff0c;可以通过触摸屏、旋钮或语音控制等方式与导航系统进行交互&#xff0c;方便驾驶者进行导航设置和操作。 实景AR导航&#xff1…

使用 classification_report 评估 scikit-learn 中的分类模型

介绍 在机器学习领域&#xff0c;评估分类模型的性能至关重要。scikit-learn 是一个功能强大的 Python 机器学习工具&#xff0c;提供了多种模型评估工具。其中最有用的函数之一是 classification_report&#xff0c;它可以全面概述分类模型的关键指标。在这篇文章中&#xff…