JavaEE初阶11.0

JVM

一、JVM

1.0 简介
(1) 简介

Java虚拟机 主要是面试题导向 jvm诞生的初心就是为了让你不用理解底层

《深入理解Java虚拟机》一书问世,面试官带火了 ,断章取义拿出来作为面试题

这本书治疗失眠效果非常好

重点理解:jvm内存区域划分 类加载机制 垃圾回收机制

2.0 内存区域
(1) 简介

为什么要进行划分区域?

JVM Java虚拟机:仿照真实的机器 真实的操作系统进行设计

真实的操作系统中,对于进程的空间地址进行了分区域的设计 JVM仿造了这样的设计8

划分不同区域之后 就有不同的分工

(2)四个核心区域

JVM具体是怎么划分的呢?

核心区域四个:

程序计数器(内存空间) 很小的区域 只是用来记录当前指令执行到哪个地址了

元数据区: 我们Java写的代码 经过Javac编译成.class文(二进制数据) class文件要加载到内存中(类加载) 而元数据区就是保存当前类被加载好的数据(类对象)~

保存方法的调用关系 每次调用方法 就会进入到方法内部执行 当方法执行完毕 返回到调用位置 继续往后面走

其实理解上来说 也是数据结构那里栈的先进后出 属于是数据结构栈的应用

虚拟机 就是虚拟一套世界~ 栈这个空间 一般来说就是几MB几十MB这样的情况(也可以更改)

保存new对象的

//这里的栈和堆不是数据结构里面的栈和堆

堆是JVM中最大的空间区域了

如果堆上的对象 不再使用了的话 就需要被释放掉(垃圾回收)

图片辅助理解:

元数据区和堆 整个Java进程共用一份 程序计数器和栈 一个进程可能有很多份(每个线程有一份)

3.0 类加载
(1) 类加载过程

站在面试角度 类加载 主要关心两个方面

类加载的步骤有哪些

三个大的阶段 第二个阶段又分成了三个步骤

a) 加载

找到.class文件 根据类的全限定名(包名+类名 形如java.lang.String)打开文件 读取文件内容并存到内存里~

b)验证:解析,校验.class文件读到的内容是否是合法的 并把这里的内容转成 结构化的数据(.class文件 二进制文件 格式是有明确要求的)

//Java官方文档:Java SE 文档 --- API 和文档 | Oracle 中国

c)准备

给类对象申请内存空间 此处申请的内存空间 相当于是"全0"的空间

d)解析:针对字符串常量 进行初始化

字符串常量 本身就包含在.class文件中 就需要.class文件里解析出来的字符串常量 放到内存空间里(元数据区 常量池中)

e)初始化

针对刚才所谈到类对象 针对类对象的各种属性进行填充 包括类中的静态成员

如果这个类还有父类 并且父类还没有加载呢 此环节也会触发父类的类加载

(背下来)

类加载中的"双亲委派模型"

//这个东西本身不是一个很紧要的机制 但是之所以成为了一个知名面试题 最主要的原因是起了一个好名字 ~ 哈哈哈 感觉很高大上 自动装箱/自动拆箱 Java内存模型 spirng里面的IOC AOP 代理模式

//补充

类加载的时机 : 可以理解为懒汉模式时机 懒汉加载 "用到的时候再加载"

构造这个类的实例 调用/使用 静态属性/静态方法 使用某个类的时候,如果他的父类还没有加载 也会触发父类的加载

(2) 双亲委派模型

双亲委派模型 描述了类加载中 根据全限定类名 找到.class文件的过程

//全限定类名:用于唯一标识一个类的完整名称

更准确的来说 单亲委派模型/父亲委派模型

* 类加载器

是一个代码块 这个代码块的功能专门就是用来进行类加载的

这个可以自定义的 可以自定义好之后 放到双亲委派模型

*过程

从appliacation入口 如果第三库里面没有 就上一级到extension里面找 以此类推

这里的爷父孙不是继承关系 是一种引用指向关系

可以按照下面的这个来进行类比

Boot可以理解为大老板 Extension可以理解为中层管理人员 Application可以理解为基层员工

4.0 垃圾回收机制(GC)
(1)引入

之前学习C语言的时候 我们了解到C语言需要申请内存malloc 申请之后一定要手动调用free释放内存空间 这样的机制 程序员在写代码的时候 总是容易忘记手动释放内存

而Java语言吸取了C的示例 jvm有自动垃圾回收机制 之后的各种编程语言也沿用了这种方式

但是自动垃圾回收机制也是有代价的: 效率下降 有的时候还是"卡了"STW问题

c++为了解决内存泄漏问题 引入了智能指针机制

rust的解决办法:通过严格的编译器检查 来对代码中的内存问题进行叫校验 编译的过程中做检查发现问题了直接报错 但是这么做也是有代价的 就是语法变得非常复杂 GC依然是主流机制

(2) 工作过程

简单来说就是找到垃圾 释放垃圾

(3) 如何找到垃圾

引用计数 (Python PHP采取的方案)

同样 这么做也是有代价的

内存消耗的更多:需要额外分配空间来记录引用记数 如果引用计数分配的空间是4个字节 newTest8个字节 这样相当于提高了50%的空间占用率

可能造成循环引用:

相当于死锁的那个情况 此时 ab这两个对象的引用不为0 虽然不为0 但是这两对象都无法使用

可达性分析(Java采取的方案)

上面的引用计数是空间换时间 这里的可达性分析是时间换空间

可达性分析的过程是:以特定对象(栈 局部变量 常量 静态成员)为起点 尽可能地遍历 判定某个对象是否能访问到

每次访问一个对象 都会把这个对象标记成"可达" 当完成所有对象的遍历之后 未被标记成"可达"的对象就是垃圾 之后回收掉

这个过程是周期性的 每隔一段时间就触发一次

下面的图片表示这个过程:

代价:每次触发 都要遍历一遍对象 如果对象很多的情况 这样以此遍历的成本也是非常大的

(4)如何释放垃圾?

释放垃圾的机制:标记-清除 复制算法 标记-整理

Java给出的答卷:分代回收 把上面的机制结合起来

标记-清除

把垃圾对象得内存 直接释放掉 这样会产生内存碎片问题

虽然存在一定的空闲空间 但是这些空闲时间不是连续的

后面我们申请内存的时候 有麻烦 因为申请内存的时候 是申请一段连续的内存空间

如果内存碎片非常多 就会导致你总的空闲时间虽然很大 但是你但凡想申请一个稍微大一点的内存

都是失败 总4G 出现上述的情况 申请1G空间都可能失败

复制算法:

一次只是使用空间的一半

把不是垃圾的对象 拷贝到另外一侧 然后再把这一侧给整体的释放掉

解决了内存碎片问题

但是也有缺点:

一旦不是垃圾的对象比较多 内存搬运数据很多的话 复制的成本也会还大

还有就是内存直接砍半 2G的内存立马变成1G(内存的空间利用率低)

标记-整理

结合了上面两个方法 既能解决内存碎片 也能保证内存利用率

删除了那些垃圾之后 把其他元素搬运到原来消除的位置

缺点:内存搬运数据的操作开销是挺大的 一旦对象很多很大 复制成本很大

分代回收:

Java给出的答卷 分代回收 把上面123种方法结合起来(主要是2 3 )

代: 对象的年龄 GC抡次 (GC轮次越多 表示越老) 某个对象经历了一轮GC可达性分析之后 不是垃圾 此时对象的年龄就+1

针对不同年龄采取不同的策略

如果某个对象 已经是年龄比较大的了 此时大概率还会继续存在很久~

通俗理解就是"要死早死了" 留下来的都是老油条 不容易死去

其实反应在生活中 就以面试为例 一个公司只有50个岗位 但是有1000份简历 公司不可能一个一个都面试笔试完毕(成本很大) 所以就先初步筛选 第一轮过滤到一大部分 然后再次细分之后

开始多轮面试 过了好几轮还在的 最后留任 公司的某位淘汰也是这个道理但是时间周期是比较长的

新创建的对象就放到"伊甸区" 绝大部分的伊甸区 都活不过第一轮GC

伊甸区=>幸存区:复制算法

幸存区中的对象:也是要经历GC的扫描 每一轮GC都会消灭一大部分对象 剩余的对象再次通过复制算法 复制到另外一个幸存区

如果这个对象在幸存区中经历了多次复制 都存活下来了 对象的年龄的就大了 就会晋升到老年代

以上就是JavaEE初阶的全部内容 好事多磨~

相关推荐
爱学习的小可爱卢3 天前
JavaEE进阶——SpringAOP从入门到源码全解析
javaee·spring-aop
sugar__salt9 天前
网络编程套接字(二)——TCP
java·网络·网络协议·tcp/ip·java-ee·javaee
努力小周10 天前
基于STM32的智能台灯系统设计与实现
stm32·单片机·嵌入式硬件·c#·毕业设计·毕设·javaee
兮山与14 天前
JavaEE初阶10.0
javaee
兮山与14 天前
JavaEE初阶8.0
javaee
兮山与14 天前
JavaEE初阶9.0
javaee
爱学习的小可爱卢17 天前
JavaEE进阶——SpringMVC响应处理详解
spring boot·postman·javaee
带刺的坐椅19 天前
Solon 不依赖 Java EE 是其最有价值的设计!
java·spring·web·solon·javaee
想不明白的过度思考者23 天前
基于 Spring Boot 的 Web 三大核心交互案例精讲
前端·spring boot·后端·交互·javaee