Java虚拟机(JVM)知识点总结

一. Java内存区域

1. JVM的内存区域划分,以及各部分的作用

可分为运行时数据区域和本地内存,按照线程私有线程共享分类:

线程私有:程序计数器、虚拟机栈、本地方法栈。

线程共享:堆、方法区、直接内存。

JDK1.7与1.8版本略有不同。

1.8中,方法区被划分到了本地内存,并以元空间的形式存在。

(1)程序计数器

主要用来依次读取指令,实现代码的流程控制;同时还可以记录当前线程的位置,使线程切换后能够恢复到正确的位置。

(2)虚拟机栈

主要用来实现Java方法的调用与执行。栈由多个栈帧组成, 每个栈帧由局部变量表、操作数栈、动态链接、方法返回地址构成。

(3)本地方法栈

主要用来实现本地方法的调用与执行。

(4)堆

线程共享的一块内存区域,主要用于存放新创建的对象实例,几乎所有对象都在这里分配内存;也是垃圾回收的主要区域,也可称为GC堆。

从垃圾回收的角度来说,堆还可细分为新生代和老年代。再细分的话,新生代有Eden区、Survivor区、Old区。

下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。

JDK1.8版本后,永久代被元空间取代。

对象的存活的区域大致可以描述为,新创建的对象在Eden区,当进行一次新生代垃圾回收后,如果对象还存活,就会进入Survivor区(S0、S1),如果年龄继续增加,就会进入老年代。

(5)方法区

JVM运行时数据区的一块逻辑区域,被线程共享。主要用来存放类信息、方法信息、常量、静态变量等。

永久代和元空间是方法区的具体实现。

 为什么要将永久代替换为元空间?

  • 永久代有一个JVM本身设置的固定大小上限,无法调整,元空间使用的本地内存,虽然也可能会发生内存溢出,但概论相对较小。
  • 元空间里存放的是类的元数据,由系统实际可用的空间来控制,能够加载更多的类。

(6)运行时常量池

用于存放编译期生成的各种字面量和符号引用的常量池表,常量池表会在类加载后存到方法区的运行时常量池中,它的功能类似于符号表。

(7)字符串常量池

为了提升性能与减少内存消耗,避免字符串的重复创建。

JDK1.7之前, 字符串常量池存放在永久代中, 为什么1.7后移动到堆中?

主要是因为永久代(方法区的具体实现)的垃圾回收效率太低,只有在Full GC时才会被GC。Java中有大量的字符串等待回收,放到堆中能够及时有效的回收字符串内存。

(8)直接内存

位于本地内存中,并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。

2. Java对象的创建过程

(1)类加载检查

new指令——检查指令参数是否能在常量池中定位到符号引用——检查符号引用代表的类是否被加载过——若没有——执行类加载过程。

(2)分配内存:

类加载通过后进行内存分配,两种方式,指针碰撞和空闲列表。选择哪种方式由Java堆是否完整决定,Java堆是否完整由采用的GC收集器是否具有压缩整理功能决定。

  • 指针碰撞

适用于堆完整。原理是用过的内存整合到一边,没用过的整合到另一边,中间有个分界指针,向着没用过的方向移动指针即可。

  • 空闲列表

适用于堆不完整。虚拟机会维护一个列表,列表中会记录哪些内存块可用,分配时,会找一个足够大的内存块分配给对象,然后更新列表。

内存分配并发问题:

  • CAS+失败重试:乐观锁。失败就重试,直到成功。这种方式保证了操作的原子性。
  • TLAB:在Eden区预留一块内存。分配内存时,先从TLAB中分配,如果不够,再用上述CAS进行分配。

(3)初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,保证了对象的实例字段在代码中可以不被赋值就直接使用。

(4)设置对象头

虚拟机对对象进行必要的设置,如对象是哪个类的实例、对象的哈希码、对象的GC分代年龄等信息,这些信息存放在对象头中。

(5)执行init方法

从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,init方法还没执行,所有字段都还为零。执行new指令后接着执行init方法,按照开发者的意愿进行初始化。

3. 什么是JVM堆溢出和栈溢出?

堆溢出指的是JVM的堆内存不足以分配新的对象时发生的溢出。

栈溢出指的是JVM虚拟栈空间不足以支持新的方法调用时发生的溢出。

二. JVM垃圾回收机制

1. 堆内存的常见分配策略

  • 对象优先在在Eden区分配。
  • 大对象直接进入老年代。
  • 长期存活的对象将进入老年代。

2. 死亡对象判断方法(是否可以被GC回收)

(1)引用计数法

给对象中添加一个引用计数器。

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效,计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的。

实现简单,效率高,但使用较少,因为无法解决对象间的循环引用问题(两个对象互相引用导致计数器不为0)。

(2)可达性分析法

以 "GC Roots" 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。

哪些对象可以作为GC Roots呢?

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象。
  • 本地方法栈(Native方法)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。

3. Java中四种引用类型

(1)强引用:大部分引用都是强引用,最普遍的引用。new出来的对象都是强引用,即使内存空间不足,也不会被GC回收。

(2)软引用:SoftReference修饰。比强引用弱一些。内存空间不足时才会回收它。

(3)弱引用:WeakReference修饰。比软引用弱。无论内存空间充足与否,只要发现了弱引用就会被GC回收。

(4)虚引用:PhantomReference修饰。最弱的引用。没有实际作用,任何时候都能被回收,主要用来跟踪对象被垃圾回收的活动。

实际中使用软引用较多,软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出等问题的产生

4. 如何判断一个常量是废弃常量?

运行时常量池主要被回收的是废弃常量。

假如在字符串常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量。

5. 如何判断一个类是无用类?

方法区主要被回收的是无用的类。

需同时满足3个条件:

  • 该类所有的实例都已被回收。
  • 加载该类的ClassLoader已被回收。
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

6. 4种垃圾收集算法

(1)标记—清除算法

标记和清除两个阶段。首先标记出所有不需要回收的对象,然后标记完成后对未标记的对象统一回收。这是基础算法,后续算法都是对其的改进。

但存在两个明显问题:效率问题(两阶段效率都不高)和空间问题(清除后存在大量不连续的内存碎片)。

(2)复制算法

为了解决效率和内存碎片问题,复制算法将内存分为大小相同的两块,每次使用其中的一块,其中一块使用完成后,将还存活的对象复制到另一块中去,然后再把使用的空间进行清理。保证每次的内存回收都是对一半区域的回收。

但依然存在问题:可用内存缩小为原来的一半。

(3)标记—整理算法

标记过程与标记—清除算法一样,但在标记完成后,让所有存活的对象向一端移动,然后清理掉端边界以为的内存。

(4)分代收集算法

主流算法。根据对象存活周期的不同将内存分为几块,一般将Java堆分为新生代和老年代,根据各个年代的特点选择合适的回收算法。

比如新生代,对象创建的多,但回收时死去的对象也很多,因此可以选择复制算法。老年代中的对象存活比较多,所以选择标记—清除或标记—整理算法。

7. 8个垃圾收集器

(1)Serial收集器

最基本、最悠久。单线程收集器,在进行垃圾收集工作时会暂停其它所有工作线程(Stop The World),直到结束。

简单而高效。

(2)ParNew收集器

Serial收集器的多线程版本,其余都和Serial一样。

(3)Parallel Scavenge收集器

几乎和ParNew收集器一样,但更关注吞吐量,高效利用CPU。

(4)Serial Old收集器

Serial收集器的老年代版本。

(5)Parallel Old收集器

Parallel Scavenge收集器的老年代版本。

(6)CMS收集器

CMS(Concurrent Mark Sweep ),以获取最短回收停顿时间为目标的收集器,注重用户体验。第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程基本上同时工作

标记清除算法的实现,运作过程大致分为四个步骤:

  • 初始标记:暂停其它所有线程,记录下与GC Root相连的对象。
  • 并发标记:同时开启GC和用户线程,用一个闭包结构记录可达对象。但GC线程无法保证可达性分析的实时性,会有引用更新的地方。
  • 重新标记:修正并发标记期间因为用户程序继续运行而导致标记产生变动的标记记录。
  • 并发清除:开启用户线程,同时GC线程对未标记的区域清除。

并发收集,低停顿。

(7)G1收集器

面向服务器的垃圾收集器,主要针对配备了多颗处理器及大容量内存的机器。既能满足低停顿还可以做到高吞吐量。

特点:

  • 并行与并发:充分利用多核的硬件优势来缩短停顿时间,也能做到和用户线程的并发执行。
  • 分代收集:不需要其它收集器配合就能独立管理整个GC堆。
  • 空间整合:整体标记—整理,局部标记—复制。
  • 可预测的停顿:除了降低停顿外,还能够建立可预测的停顿时间模型,让用户指定停顿时间。

步骤:

  • 初始标记
  • 并发标记
  • 最终标记
  • 筛选回收

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region,这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率。

JDK9之后,G1变成了默认的GC。

(8)ZGC收集器

采用复制算法,将暂停时间控制在几毫米以内,且暂停时间不受内存堆大小的影响,代价是牺牲了一些吞吐量。

8. Minor GC和Full GC

Minor GC主要发生在新生代,频繁且速度快;Full GC主要发生在老年代,回收速度较慢。一般来说,对象在新生代的Eden区分配。当Eden区没有足够空间分配时,虚拟机会进行⼀次Minor GC,Minor GC之后survivor放不下,要放到老年代,此时发现老年代也放不下,就会触发Full Gc。

三. 类加载器

1. 类加载器有哪些?

类加载过程:加载—连接—初始化。

连接又分为三步:验证—准备—解析。

类加载器作用于第一步加载。

类加载器的主要作用就是加载 Java 类的字节码(. class文件)到 JVM 中(在内存中生成一个代表该类的Class对象)。

JVM中内置了三个重要的ClassLoader:

  • BootstrapClassLoader(启动类加载器):最顶层的加载类,加载核心库:JAVA_HOME/jre/lib目录下的库。
  • ExtensionClassLoader(扩展类加载器) :加载扩展类的类库;JAVA_HOME/jre/lib/ext。
  • AppClassLoader(应用程序类加载器) :面向用户的加载器,加载classpath下的类。
  • 自己编写的类加载器。

除了 BootstrapClassLoader,其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader。如果要自定义自己的类加载器,也需要继承 ClassLoader 抽象类。

2. 双亲委派模型

类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载呢?

双亲委派模型和上面提到的类加载器层次关系图一致。

大致流程:加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,继续向上委托,如果这个类委托的上级没有被加载,子加载器会尝试加载这个类

  • 在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。
  • 加载的时候,首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父类加载器的loadClass()方法来加载类),所有的请求最终都会传送到顶层的启动类加载器BootstrapClassLoader中。
  • 当父类加载器无法处理这个加载请求(它的搜索范围中没有找到所需的类),子加载器才会尝试自己加载(调用自己findClass()方法来加载类)。
  • 子加载器也无法加载,抛出ClassNotFoundException异常。

双亲委派模型的好处?

  • 保证Java程序的稳定运行,避免重复加载类(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类)
  • 保证Java的核心API不被篡改。比如自己编写了一个java.lang.Object类,程序运行时就会有两个不同的Object类。双亲委派模型可以保证加载的时JRE里的Object类,而不是自己写的。因为AppClassLoader加载自己写的Object类时,会先委派给它的父类,即ExtClassLoader,而Ext又会委派给Boot,Boot发现自己加载过Object类了,会直接返回,而不是加载自己写的。

如何打破双亲委派模型?

继承ClassLoader类,自定义一个加载器,然后重写 loadClass() 方法。

之所以重写loadClass方法是因为加载类时,类加载器会先委托父类加载器去完成,即调用父类的loadClass()方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web APIs知识点讲解(阶段四)

DOM- 事件高级 一.回顾(购物车案例) <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><meta http-equiv&qu…

NRF52832修改OTA升级时的bootloader蓝牙MAC

NRF52832在OTA升级时&#xff0c;修改了APP的蓝牙MAC会导致无法升级&#xff0c;原因是OTA程序的蓝牙MAC没有被修改所以手机扫描蓝牙时无法连接 解决办法 在bootloader的程序里面加入修改蓝牙mac地址的代码实现原理&#xff1a; 在bootloader蓝牙广播开启之前修改蓝牙mac 通…

接口自动化框架搭建(九):接入钉钉消息通知

1&#xff0c;jenkins安装钉钉插件 2&#xff0c;在钉钉群聊设置机器人 3&#xff0c;jenkins配置钉钉 根据情况选择&#xff1a; 除了这些&#xff0c;其他不用配置&#xff0c;配置完成点击确认 4&#xff0c;项目配置 添加后保存 5&#xff0c;测试下效果 构建完成后&a…

缓存数据库的意义、作用与种类详解

在现代计算机应用中&#xff0c;数据访问的性能往往是关键因素之一。随着数据量的增加和复杂应用的兴起&#xff0c;数据库的访问成本逐渐成为瓶颈。为了提高应用程序的响应速度、减轻后端数据库的负载压力&#xff0c;缓存数据库应运而生。 什么是缓存数据库&#xff1f; 缓存…

Cesium.js综合实验

Cesium.js综合实验 1 概述 Cesium是一个跨平台、跨浏览器的展示三维地球和地图的开源 JavaScript 库&#xff0c;是AGI公司计算机图形开发小组与2011年研发的三维地球和地图可视化开源JavaScript库&#xff0c;Cesium一词来源于化学元素铯&#xff0c;铯是制造原子钟的关键元…

深度学习| DiceLoss解决图像数据不平衡问题

图像数据不平衡问题 图像数据不平衡&#xff1a;在进行图像分割时&#xff0c;二分类问题中&#xff0c;背景过大&#xff0c;前景过小&#xff1b;多分类问题中&#xff0c;某一类别的物体体积过小。在很多图像数据的时候都会遇到这个情况&#xff0c;尤其是在医学图像处理的…

【ctf.show】--- md5

ctf.show_web9 1.用 dirsearch 扫目录&#xff1a;python dirsearch.py -u 网址 -e php 发现 robots.txt 2.访问 robots.txt 文件 发现 index.phps 3.访问 index.phps 发现源码 <?php $flag""; $password$_POST[password]; if(strlen…

JSP技术及其应用

目录 一、JSP 指令元素 1. page指令 二、JSP 注释 1. HTML注释&#xff1a; 2. Java注释&#xff1a; 3. JSP注释&#xff1a; 三、页面编码格式 1. pageEncoding&#xff1a; 2. contentType&#xff1a; 一、JSP 指令元素 JSP包含三种主要的指令元素&#xff1a;pag…

开源,微信小程序-超级计算器T3000 简介

笔者于四年前自学微信小程序开发&#xff0c;这个超级计算器T3000就是当时的练习作品。超级计算器T3000的功能有很多&#xff0c;其中的核心技术是矩阵计算&#xff0c;使用的工具库是math.js&#xff0c;其次是复杂运算和分式运算。关于math.js的使用&#xff0c;可以参考另一…

【快速上手ESP32(基于ESP-IDFVSCode)】01-环境配置GPIO口延时函数(先点个灯)

前言 立创开发板之前出了个ESP32S3R8N8的开发板&#xff0c;当时嫖了个优惠券&#xff0c;九块九拿下了。 现在不仅是35.9一个&#xff0c;而且还没货。 如果真的想要的小伙伴可以自己去打板焊一个&#xff0c;人家开源了&#xff08;目测难度较大&#xff0c;反正我是焊不上…

Flink CDC 同步数据到Doris

Flink CDC 同步数据到Doris Flink CDC 是基于数据库日志 CDC(Change Data Capture)技术的实时数据集成框架,支持了全增量一体化、无锁读取、并行读取、表结构变更自动同步、分布式架构等高级特性。配合 Flink 优秀的管道能力和丰富的上下游生态,Flink CDC 可以高效实现海量…

如何在Win10部署Oracle数据库并实现无公网IP使用PL SQL远程访问

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

[RAM] 图解 RAM 结构原理

主页&#xff1a; 元存储博客 文章目录 前言1. Channel2. Dimm3. Rank4. Bank5. Row6. Column7. Beat8. Burst Length总结 前言 从CPU至DRAM晶粒之间依据层级由大至小为channel>DIMM>rank>chip>bank>row/column。 图片来源&#xff1a; 电脑王 DRAM层级关系 DR…

第十五届蓝桥杯第三期模拟赛第十题 ← 上楼梯

【问题描述】 小蓝要上一个楼梯&#xff0c;楼梯共有 n 级台阶&#xff08;即小蓝总共要走 n 级&#xff09;。小蓝每一步可以走 a 级、b 级或 c 级台阶。 请问小蓝总共有多少种方案能正好走到楼梯顶端&#xff1f;【输入格式】 输入的第一行包含一个整数 n 。 第二行包含三个整…

go发布包到github

1. 首先&#xff0c;我们在github上创建一个公有仓库并clone到本地 git clone https://github.com/kmust-why/gdmp-token.git cd gdmp-token/ 2. 在gdmp-token工程中初始化go.mod&#xff0c;其中后面的链接要跟github上创建的仓库和你的用户名对应 go mod init github.com…

python 字典练习

# 字典练习1 import time def main():month_income{1月: 8000, 2月: 8200, 3月: 7900, 4月: 6900, 5月: 8900, 6月: 12000, 7月: 8900, 8月: 6000,9月: 8900, 10月: 9200, 11月: 6200, 12月: 7000}year_income0for k,v in month_income.items():print(月份→,k,工资→,v)time.s…

4.模板-数组类封装

文章目录 功能代码运行结果 功能 利用模板进行数组类封装&#xff0c;在代码实现过程中&#xff0c;使用了 1.operator重载&#xff0c;利用深拷贝防止浅拷贝问题&#xff1b; 2.operator[]重载&#xff0c;确保可以使用[]来仿真数组元素&#xff1b; 3.尾插法、尾删法、返回数…

基于主成分分析的机器学习分类代码

前言 本文内容主要实现基于主成分分析的数据降维和四种经典的机器学习分类算法&#xff0c;包括&#xff1a;支持向量机、随机森林、XGBoost分类器、scikit-learn的梯度提升分类器和Histogram-based Gradient Boosting分类器 1.数据准备 import pickle import pandas as pd …

消息队列RocketMQ环境搭建

消息队列RocketMQ环境搭建 1.下载:配置环境变量启动NameServer启动Broker发送和接收消息测试模拟发送消息模拟接收消息 控制台安装与启动 软硬件需求: 系统要求是 64 位的&#xff0c;JDK要求是1.8及其以上版本的 1.下载: https://rocketmq.apache.org/download/ 2.解压到指…

fast_bev学习笔记

目录 一. 简述二. 输入输出三. github资源四. 复现推理过程4.1 cuda tensorrt 版 一. 简述 原文:Fast-BEV: A Fast and Strong Bird’s-Eye View Perception Baseline FAST BEV是一种高性能、快速推理和部署友好的解决方案&#xff0c;专为自动驾驶车载芯片设计。该框架主要包…