JavaEE:JVM

news2024/11/24 15:46:18

基本介绍

JVM:Java虚拟机,用于解释执行Java字节码

jdk:Java开发工具包

jre:Java运行时环境

C++语言将写入的程序直接编译成二进制的机器语言,而java不想重新编译,希望能直接执行。Java先通过javac把.java文件转成.class文件,.class文件是字节码文件,包含Java字节码,然后Java把这个字节码文件在某个具体的平台上执行。此时再通过jvm把上述的字节码转成对应的CPU能识别的机器指令

当前主流的JVM:HotSpot VM


JVM中的内存区域划分

JVM其实也是一个进程,就是在任务管理器中能看到的Java进程

进程运行的过程中,要从操作系统申请一些资源(典型资源:内存),申请到的内存空间可以支撑后续Java程序的执行。

比如:在Java中定义变量,就会申请内存,这里的申请就是由jvm完成的

而jvm申请的这一块内存还会根据实际的用途划分出不同的空间(区域划分)

1.堆:代码中new出来的对象,对象中持有的非静态成员变量都是在堆里(只有一份)

2.栈:本地方法栈包含了方法调用关系和局部变量,虚拟机栈记录了Java代码的调用关系,Java代码的局部变量(一般提到的栈指的是虚拟机栈)(可以有n份,n与线程相关)

这里的堆和栈和数据结构里的不一样

这里的堆和栈都是内存区域,而数据结构的堆是一颗二叉树,栈是后进先出的数据结构

3.程序计数器:空间比较小,存储下一条要执行的Java指令的地址(有n份)

x86的CPU也有一个类似的寄存器:eip

4.元数据区:保存类的信息和方法的信息 (只有1份)

类的信息:类的名称,继承哪个类,实现的接口;有什么属性,属性名字,属性类型,权限;有什么方法,方法名字,方法参数,权限等等

“元数据(meta data)”:往往指一些描述性质或者辅助性质的属性


笔试题

class Test{
    private int n;
    private static int m;
}

main(){
    Test t = new Test();
}

上述代码里的t, n, m各自处于jvm哪个区域

t是一个局部变量(引用类型),这个变量在栈上

n是Test的成员变量,处于堆上

m是static修饰的变量,称为类属性,存在类对象中,也属于元数据区


JVM的类加载机制

类加载:Java进程运行的时候需要把.class文件从硬盘读取到内存并进行一系列的校验解析的过程

过程(5个步骤)

1.加载:把硬盘上的.class文件找到打开文件,读取文件内容(读到的是二进制数据)

如何查找对应的文件?双亲委派模型(一种查找策略)

2.验证:确保读到的文件是合法的.class文件(验证依据:Java的虚拟机规范)

3.准备:给类对象申请内存空间,申请到的内存空间里面的默认值是0

4.解析:针对类中字符串常量进行处理,将常量池中的符号替换成直接引用的过程

class Test{
    private String s = "hello";
}

 

在代码中我们知道s相当于包含了"hello"字符串常量的地址,但是在文件中是不存在"地址"这样的概念的。地址是内存的地址,硬盘里没有地址。

虽然没有地址,但是我们可以存储一个类似于地址的偏移量

把hello字符串的开头到文件开头就是一个偏移量

此处文件中填充给s的hello的偏移量就认为是符号引用,接下来把.class文件加载到内存中,先把"hello"这个字符串加载到内存中,此时“hello”就有地址了,s里面的值就可以替换成当前“hello”真实的地址了,可以直接引用这个地址了

5.初始化:针对类对象完成后续的初始化(要执行代码块的逻辑,甚至会触发父类的加载)


双亲委派模型(重点)

JVM中的类加载操作有一个专门的模块:类加载器

作用:给定全限定类名,也就是带有包名的类名,比如java.lang.String就是一个全限定类名,能找到.class文件

默认有三个

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

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

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

上述三个类加载器存在父子关系,这个父子关系类似于二叉树,有一个指针parent,指向自己的父类加载器

双亲委派模型的工作过程:

1.从ApplicationClassLoader作为入口,先开始工作

2.ApplicationClassLoader不会立即搜索自己负责的目录,会把搜索的任务交给自己的父亲

3.代码进入到ExtensionClassLoader的范畴,ExtensionClassLoader也不会立即搜索自己负责的目录,会把搜索任务交给父亲

4.BoostrapClassLoader没有父亲,没办法推脱搜索任务了,才会真正搜索自己负责的标准库目录。通过全限定类名,尝试在标准库目录中找到符合要求的.class文件

找到了就直接进入打开文件和读文件的流程;如果没到找,返回给孩子类加载器,继续尝试加载

5.ExtensionClassLoader收到交回的任务后,在自己负责的扩展库目录搜索,找到了进入后续流程,没找到再丢给自己孩子

6.ApplicationClassLoader收到交回的任务后,自己进行搜索负责的目录。再找不到就抛出ClassNotFoundException 异常

上述执行顺序的好处:

1)确保几个类加载器之间的优先级

2)用户自定义的类不会被jvm加载,可以防止自定义类不小心和标准库中的类名字重复


JVM的垃圾回收机制(GC机制)

基本情况

这个机制不需要程序员手动释放内存。程序回自动判断某个内存是否会继续使用,如果内存后续不用了,就会自动释放掉。

垃圾回收中一个重要问题:STW(stop the world)问题——触发垃圾回收的时候,可能会使当前程序的其他业务逻辑被暂停

垃圾回收内存的话,那内存里面几个区域里面情况如何?

1)程序计数器 -- 不需要GC

2)栈 -- 不需要GC,因为局部变量在代码块执行结束之后自动销毁

3)元数据区/方法区 -- 不需要GC,因为一般都是涉及类加载而不是类卸载

4)堆 -- GC的主要工作区域

所以,垃圾回收回收内存,不如说是回收对象(对象也是回收的单位)


工作机制

1.识别出垃圾

判定哪些对象不再使用,哪些对象还在使用

在Java中的对象一定要通过引用的方式来使用,除非匿名对象。如果一个对象没有任何引用指向它,就可以认为无法被代码引用,就可以作为垃圾了

void func(){
    Test t = new Test();
    t.do();
}

这里通过new Test在堆上创建了对象,与此同时在栈上也存储下这个局部变量

当执行到 “}” 的时候,t这个局部变量在栈中自动销毁。上面的new Test()对象就没有引用指向它了。此时这个对象就成为了垃圾

如果代码复杂一点呢?

Test t1 = new Test();
Test t2 = t1;
t3 = t2;
t4 = t3;

此时会有很多引用指向new Test同一个对象,需要确保所有的引用都销毁了才能把Test对象视为垃圾。如果代码再复杂,每个引用的生命周期各不相同,那怎么办呢?

解决办法:

1)引用计数:给每个对象安排一个额外的空间,空间里要保存当前这个对象有几个引用

每次有一个引用指向这个对象,引用计数就+1,制空或者删除一个引用,引用计数就-1

此时有专门的扫描线程去获取当前每个对象的引用计数情况,如果发现对象的引用计数为0,说明这个对象可以被释放了

这个方法虽然没有在JVM中使用,但是广泛应用于其他语言的垃圾回收机制中,比如python和PHP

问题一:每个对象分配到的计数器消耗了额外的内存空间,对象数目一多空间资源容易不足

问题二:引用计数可能会产生“循环引用的问题”

此时两个对象,引用计数都不是0,不能被GC回收掉,但是这两个对象又无法使用 -- 类似于死锁


2)可达性分析(JVM用的是这个)

本质上用时间换空间。在写代码的时候会定义很多变量,就可以从这些变量作为起点,尝试进行遍历(沿着这些变量中持有的引用类型的成员,再进一步地往下进行访问),所有能被访问的对象就不是垃圾了,剩下的遍历一圈也访问不到的对象就是垃圾

虽然这个代码中只有一个root的引用,但是7个结点的对象都是可达的。JVM中存在扫描线程,会尽可能多的去遍历访问对象

如果root.right = null的话,a跟c之间就会断开,那么按照上述方法遍历的操作就无法访问到c和f了,此时c和f节点对象就不可达,不可达就变成垃圾了


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

释放方式

1)标记 - 清除

把标记成垃圾的对象直接释放掉(一般不使用)

产生的问题:内存碎片 -- 小的但是离散的空闲内存空间

会导致后续的内存申请失败。因为我们的内存申请时一次性申请一个连续的空间,比如我们申请1M的内存空间,此时的1M字节都是连续的

如果有很多内存碎片就可能导致空闲空间总和超过1MB,但是没有比1MB大的连续空间,申请就会失败


2)复制算法

核心是不直接释放内存,而是把内存划分成为两半

把不是垃圾的对象复制到内存的另一半里,接下来就把左侧的空间(原来垃圾存在的空间)整体释放掉

比如我们要删掉对象2和4,我们会把不需要删除的1和3复制一份到右半边内存

然后把左半边全删掉

优点:规避内存碎片问题

缺点:1)总的可用内存变少;2)如果每次要复制的对象很多,复制的开销很大(所以这个算法适用情况:当前这轮GC中要删掉的对象很多,存活的对象很少)


3)标记 - 整理

类似顺序表删除中间的元素(搬运思想)

比如下面要删除1,3,6

接着把2,4,5,7,8往前搬运

优点:解决内存碎片问题,不需要过多浪费内存空间

缺点:复制开销很大


4)JVM采用的综合方案:分代回收(重点)

引入概念:对象的年龄(初始年龄为0)

JVM中有专门的线程负责周期性扫描或释放。一个对象如果被线程扫描了一次,发现是可达了,该对象的年龄+1

JVM会根据对象年龄的差异,把整个堆内存分成两个大的部分,分别为新生代(年龄小的对象)和老年代(年龄大的对象)

具体流程:复制算法+标记 - 整理

a)当代码中new处理一个新的对象,这个对象就被放在伊甸区

一个经验规律:大部分伊甸区的对象是朝生夕死的,活不过第一轮GC

b)第一轮GC扫描之后,少数幸存的对象就会通过复制算法拷贝到生存区,后续GC扫描线程继续扫描,生存区中大部分对象也会在扫描中被标记为垃圾,少数存活的会被拷贝到另一半的生存区中每经历一轮扫描,生存的对象年龄+1

c)如果这个对象在生存区中经过若干轮GC之后还存活着,JVM会认为这个对象的生命周期很长,就会将其从生存区拷贝到老年代

d)老年代的对象也要参与扫描,但是被扫描的频率大大降低

e)对象在老年代寄掉了,JVM就将其释放了

常使用的垃圾收集器:GMS, G1和ZGC

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

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

相关文章

【机器学习】贝叶斯算法在机器学习中的应用与实例分析

贝叶斯算法在机器学习中的应用与实例分析 一、贝叶斯算法原理及重要性二、朴素贝叶斯分类器的实现三、贝叶斯网络在自然语言处理中的应用四、总结与展望 在人工智能的浪潮中,机器学习以其独特的魅力引领着科技领域的创新。其中,贝叶斯算法以其概率推理的…

用于密集视觉冲击的紧凑三维高斯散射Compact 3D Gaussian Splatting For Dense Visual SLAM

Compact 3D Gaussian Splatting For Dense Visual SLAM 用于密集视觉冲击的紧凑三维高斯散射 Tianchen Deng 邓天辰11Yaohui Chen 陈耀辉11Leyan Zhang 张乐妍11Jianfei Yang 杨健飞22Shenghai Yuan 圣海元22Danwei Wang 王丹伟22Weidong Chen 陈卫东11 Abstract 摘要 …

通过腾讯云搭建跨境电商demo的详细操作过程(建站系统 保姆级指导,巨详细)

引言: 有许多做跨境电商的朋友,或者为跨境电商服务的小企业,都会面临搭建电商平台V1.0的问题 因此,花了点时间,找了一个开源的项目,让大家可以跑起来,一方面了解平台都有哪些模块,另…

Unity 左右折叠显示与隐藏UI的简单实现

要实现一个简单的UI左右折叠显示与隐藏,可以结合遮罩,通过代码控制UI区块的宽度和位移来实现。 具体可以按以下步骤实现: 1、新建一个Image组件,并添加精灵,调整大小后,复制一份作为该UI的父物体&#xf…

1、MYSQL系列-深入理解Mysql索引底层数据结构与算法

索引的本质 索引是帮助MySQL高效获取数据的排好序的数据结构 索引数据结构 二叉树红黑树Hash表BTree B-Tree B-Tree 叶节点具有相同的深度,叶节点的指针为空,所有索引元素不重复,节点中的数据索引从左到右递增排列 BTree(B-Tree变种) 非叶…

Pytorch搭建GoogleNet神经网络

一、创建卷积模板文件 因为每次使用卷积层都需要调用Con2d和relu激活函数,每次都调用非常麻烦,就将他们打包在一起写成一个类。 in_channels:输入矩阵深度作为参数输入 out_channels: 输出矩阵深度作为参数输入 经过卷积层和relu激活函数…

Qt对象池,单例模式,对象池可以存储其他类的对象指针

代码描述: 写了一个类,命名为对象池(ObjectPool ),里面放个map容器。 3个功能:添加对象,删除对象,查找对象 该类只构建一次,故采用单例模式功能描述:对象池可…

04 MySQL --DQL 专题--Union、exists

1. UNION、UNION ALL UNION 关键字的作用? 合并两个或多个 SELECT 语句的结果。发挥的作用与 or 非常相似 UNION关键字生效的前提? 每个 SELECT 语句必须拥有相同数量的列。每个 SELECT 语句中的列的顺序必须相同。列必须拥有相似的数据类型。 SELEC…

WebRTC直播间搭建记录

考虑到后续增加平台直播的可能性,笔记记录一下WebRTC相关. 让我们分别分析两种情况下的WebRTC连接建立过程: 情况一:AB之间可以直接通信 1.信令交换: 设备A和设备B首先通过信令服务器交换SDP(Session Description Pr…

负载均衡集群——LVS

目录 1.LVS简介 2.LVS体系结构 3.LVS相关术语 4. LVS工作模式 5. LVS调度算法 6.LVS集群介绍 6.1 LVS-DR模式 6.2 LVS – NAT 模式 6.3 LVS – TUN 模式 7.LVS 集群构建 7.1 LVS/NAT 模式配置 实验操作步骤 步骤 1 Nginx1 和 Nginx2 配置 步骤 2 安装和配置 LVS …

R语言使用installr包对R包进行整体迁移

今天分享一个R语言的实用小技巧,如果咱们重新安装了电脑(我重装了电脑)或者因为需要卸载旧版本的R软件,安装新版本的R,那么必然会造成R包的库缺失,需要重新下载,有些还不是官方的R包&#xff0c…

如何从零开始创建React应用:简易指南

🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…

认识异常(1)

❤️❤️前言~🥳🎉🎉🎉 hellohello~,大家好💕💕,这里是E绵绵呀✋✋ ,如果觉得这篇文章还不错的话还请点赞❤️❤️收藏💞 💞 关注💥&a…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之十三 简单去除图片水印效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之十三 简单去除图片水印效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之十三 简单去除图片水印效果 一、简单介绍 二、简单去除图片水印效果实现原理 三、简单去除图片水印效果案例…

stm32实现hid键盘

前面的cubelmx项目配置参考 stm32实现hid鼠标-CSDN博客https://blog.csdn.net/anlog/article/details/137814494?spm1001.2014.3001.5502两个项目的配置完全相同。 代码 引用 键盘代码: 替换hid设备描述符 先屏蔽鼠标设备描述符 替换为键盘设备描述符 修改宏定…

Springboot框架——3.整合SpringMVC

1.修改端口号: 在application.properties中添加如下配置即可: server.port8088 2.静态资源访问: 首先打开ResourceProperties这个类的源码: 将静态资源放到类中默认位置即可实现访问: http://localhost:8088/erth.jp…

Docker安装xxl-job分布式任务调度平台

文章目录 Docker安装xxl-job分布式任务调度平台1.xxl-job介绍2. 初始化“调度数据库”3、docker挂载运行xxl-job容器3.1、在linux的opt目录下创建xxl_job文件夹,并在里面创建logs文件夹和application.properties文件3.2、配置application.properties文件&#xff0c…

springboot整合dubbo实现RPC服务远程调用

一、dubbo简介 1.什么是dubbo Apache Dubbo是一款微服务开发框架,他提供了RPC通信与微服务治理两大关键能力。有着远程发现与通信的能力,可以实现服务注册、负载均衡、流量调度等服务治理诉求。 2.dubbo基本工作原理 Contaniner:容器Provider&#xf…

安全中级-环境安装(手动nginx以及自动安装php,mysql)

为了方便大家跟bilibili课程,出了第一节环境 bilibili搜凌晨五点的星可以观看相关的教程 一、环境 ubentu 二、nginx手动安装 2.1第一步 wget https://nginx.org/download/nginx-1.24.0.tar.gz 2.2下载好安装包以后解压 tar -zxvf nginx-1.21.6.tar.gz2.3安…

Python零基础从小白打怪升级中~~~~~~~TCP网络编程

TCP网络编程 一、什么是TCP协议 TCP( Transmission control protocol )即传输控制协议,是一种面向连接、可靠的数据传输协议,它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。 面向连接 :数据传输之前客户端和…