JavaEE:JVM

基本介绍

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.rhkb.cn/news/312001.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio 2019 社区版下载

一、网址 https://learn.microsoft.com/zh-cn/visualstudio/releases/2019/release-notes#start-window 二、选择这个即可

【Java EE】关于Spring MVC 响应

文章目录 🎍返回静态页面🌲RestController 与 Controller 的关联和区别🌴返回数据 ResponseBody🎋返回HTML代码片段🍃返回JSON🍀设置状态码🎄设置Header🌸设置Content-Type&#x1f…

【单例模式】饿汉式、懒汉式、静态内部类--简单例子

单例模式是⼀个单例类在任何情况下都只存在⼀个实例,构造⽅法必须是私有的、由⾃⼰创建⼀个静态变量存储实例,对外提供⼀个静态公有⽅法获取实例。 目录 一、单例模式 饿汉式 静态内部类 懒汉式 反射可以破坏单例 道高一尺魔高一丈 枚举 一、单例…

自学Java的第二十四次笔记

一,方法重载 1.基本介绍 java 中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致! 比如: System.out.println(); out 是 PrintStream 类型 2.重载的好处 1) 减轻了起名的麻烦 2) 减轻了记名的麻烦 3.快速入门案…

【中间件】ElasticSearch简介和基本操作

一、简介 Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,支持各种数据类型,包括文本、数字、地理、结构化、非结构化 ,可以让你存储所有类型的数据,能够解决不断涌现出的各种用例。其构成如下: 说明&#xff1…

Python基于深度学习的车辆特征分析系统

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

ARMv8-A架构下的外部debug模型之外部调试事件(external debug events)概述

外部调试器与处理器之间的握手与external debug events 一,External Debug的使能二,外部调试器和CPU之间的握手三,外部调试事件 External debug events1. External debug request event2. Halt instruction debug event3. Halting step debug…

天池酒瓶瑕疵检测数据集分析及完整baseline

以下内容为还没思路的小伙伴牵个头提供一个demo,大佬勿喷,线上成绩0.7,留空间给小伙伴们发挥自己的力量 ps:markdown不怎么熟悉,代码中如有明显缩进问题,自行斟酌改正,编辑好几次都改不过来,请原谅.... 数据分析瑕疵大类: 瓶盖瑕疵、标贴瑕疵、喷码瑕疵、瓶身瑕疵、酒液瑕疵瑕…

hadoop编程之工资序列化排序

数据集展示 7369SMITHCLERK79021980/12/17800207499ALLENSALESMAN76981981/2/201600300307521WARDSALESMAN76981981/2/221250500307566JONESMANAGER78391981/4/22975207654MARTINSALESMAN76981981/9/2812501400307698BLAKEMANAGER78391981/5/12850307782CLARKMANAGER78391981/…

Spectral Adversarial MixUp for Few-Shot Unsupervised Domain Adaptation论文速读

文章目录 Spectral Adversarial MixUp for Few-Shot Unsupervised Domain Adaptation摘要方法Domain-Distance-Modulated Spectral Sensitivity (DoDiSS)模块Sensitivity-Guided Spectral Adversarial Mixup (SAMix)模块 实验结果 Spectral Adversarial MixUp for F…

Python也可以合并和拆分PDF,批量高效!

PDF是最方便的文档格式,可以在任何设备原样且无损的打开,但因为PDF不可编辑,所以很难去拆分合并。 知乎上也有人问,如何对PDF进行合并和拆分? 看很多回答推荐了各种PDF编辑器或者网站,确实方法比较多。 …

k-means聚类算法的MATLAB实现及可视化

K-means算法是一种无监督学习算法,主要用于数据聚类。其工作原理基于迭代优化,将数据点划分为K个集群,使得每个数据点都属于最近的集群,并且每个集群的中心(质心)是所有属于该集群的数据点的平均值。以下是…

LabVIEW变速箱自动测试系统

LabVIEW变速箱自动测试系统 在农业生产中,采棉机作为重要的农用机械,其高效稳定的运行对提高采棉效率具有重要意义。然而,传统的采棉机变速箱测试方法存在测试效率低、成本高、对设备可能产生损害等问题。为了解决这些问题,开发了…

深度学习 Lecture 8 决策树

一、决策树模型(Decision Tree Model) 椭圆形代表决策节点(decison nodes),矩形节点代表叶节点(leaf nodes),方向上的值代表属性的值, 构建决策树的学习过程: 第一步:决定在根节点…

windows关闭Windows Search功能

我发现windows最恶心的功能就是自动更新和搜索。自动更新就是个毒瘤,得到了全世界的人讨厌。 而搜索功能难用、慢和造成卡死,根本没有存在的必要。并且他的windows search filter服务会在每次移动大量文件后建立索引,持续的占用cpu和硬盘的资…

基于Springboot的影城管理系统

基于SpringbootVue的影城管理系统的设计与实现 开发语言:Java数据库:MySQL技术:SpringbootMybatis工具:IDEA、Maven、Navicat 系统展示 用户登录 首页展示 电影信息 电影资讯 后台登录页 后台首页 用户管理 电影类型管理 放映…

【Redis 神秘大陆】003 数据类型使用场景

三、Redis 数据类型和使用场景 Hash:对象类型的数据,购物车List:队列/栈Set:String类型的无序集合,intset,抽奖、签到、打卡,商品评价标签Sorted Set:存储有序的元素,zip…

跨境物流系统解决方案:构建全球化供应链的关键步骤

随着全球化的发展,跨境物流已成为国际贸易中不可或缺的重要环节。然而,由于各国之间的政治、法律、文化和语言差异,跨境物流常常面临诸多挑战,如货物清关、运输安全、物流跟踪等问题。因此,构建一个高效、可靠的跨境物…

什么是结构分析法

全面深刻认识经济社会现象,仅仅考察总量及其变化是不够的,还要考察经济社会系统中各构成部分及其对比关系的变动。 一、基本概念 结构分析法是在统计分组的基础上,计算各组成部分所占比重,进而分析某一总体现象的内部结构特征依时…