JVM 内存和 GC 算法
文章目录
- 内存布局
- 直接内存
- 执行引擎
- 解释器
- JIT 即时编译器
- JIT 分类
- AOT 静态提前编译器(Ahead Of Time Compiler)
- GC
- 什么是垃圾
- 为什么要GC
- 垃圾回收行为
- Java GC 主要关注的区域
- 对象的 finalization 机制
- GC 相关算法
- 引用计数算法(Reference Counting)
- 可达性分析算法
- GC Roots
- Stop The World
- 标记-清除算法(mark-sweep)
- 复制算法(Copying)
- 标记-压缩(标记-整理)算法
- 分代垃圾回收
- 增量收集算法
- 分区算法
内存布局
- 对象头(Header)
- 运行时元数据(Mark word):哈希值、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
- 类型指针 :指向类元数据,确定对象所属类型。如果是数组还要记录数组的长度
- 实例数据(Instance Data):类中的各类型变量以及父类的相关类型数据。
- 对齐填充(Padding):非必须,也没有特殊含义,仅起到占位符的作用
直接内存
- 直接内存不是JVM 运行时数据区的一部分,是 Java 堆外的,系统的内存区间。NIO 通过存在堆中的 DirectByteBuffer 操作 Native 内存。通常情况下,直接内存的运行效率优于 Java 堆,读写频繁的场合,如果NIO 库就允许 Java 程序使用直接内存。
直接内存在 Java 堆外,因此是不受 -Xmx 的限制的,但操作系统内存是有限的
- -XX:MaxDirectMemorySize=1G,表示设置 NIO 可以操作的直接内存最大大小为 1G,默认为 0,表示 JVM 自动选择 NIO 可以操作的直接内存大小。
直接内存也会 OOM。
执行引擎
执行引擎(Execution Engine),将字节码指令解释/编译(注意与 Java 文件编译为 .class 文件的编译区分,有的地方称之为后端编译)为对应平台上的本地机器指令,实际就是将高级编程语言翻译为机器指令。
解释器
当 Java 虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件内容“翻译”为对应平台的本地机器指令。此过程由解释器执行。
- 字节码解释器:纯软件代码模拟字节码执行,效率低下。
- 模板解释器:每一条字节码和一个模板函数相关联,模板函数直接产生这条字节码执行时的机器码,效率高。
基于解释器来执行,还是相对低效的,所以又有了 JIT 即时编译器。
JIT 即时编译器
JIT(Just In Time Compiler)即时编译器:就是 JVM 将源代码直接编译成和本地机器相关的机器语言。
- 根据代码被调用的执行频率来确定是否需要启动 JIT 来进行编译,这些需要被 JIT 编译为本地指令的代码,称为“热点代码”,JIT 在运行时会针对这些热点代码做深度优化,以提高 Java 程序的性能。
- 栈上替换:一个多次被调用的方法,或者一个方法体内部的循环次数较多的循环体,都可以称为“热点代码”,他们都可以通过 JIT 编译为本地机器指令。由于此过程发生在方法执行过程中,因此也称为栈上替换(OSR 编译)On Stack Replacement。
- 热点探测功能:HotSpot 基于计数器的方式来进行热点探测热点代码被调用的次数。Client 模式 1500次,Server 模式 10000 次,才是热点代码,才切换为 JIT 编译。
- 指令缓存:JIT 编译为本机指令代码后,会进行代码缓存,以提高性能。
此外我们还可以通过 java 命令设置,让程序使用纯解释器或纯 JIT 编译器执行,或者两者的混合执行的模式。
- -Xint 纯解释器模式
- -Xcomp 纯编译器模式
- -Xmixed 混合模式(默认)
JIT 分类
JIT 分为如下两类编译器:
- C1(Client Compiler)编译器:运行在 -client 模式下,C1 编译器会对字节码进行简单和可靠的优化,耗时短。
- 方法内联
- 去虚拟化
- 冗余消除
- C2(Server Compiler)编译器:运行在 -server 模式下,C2 进行耗时较长的优化,以及激进优化。但优化后的代码执行效率高。
- 标量替换
- 栈上分配
- 同步消除
- Graal 编译器:JDK 10+ 才加入的全新的即时编译器。
AOT 静态提前编译器(Ahead Of Time Compiler)
JIT 是程序运行中执行的,AOT 编译是在程序运行之前执行,将字节码转换为机器码的过程。好处:预编译.class 文件为 .os 文件
GC
什么是垃圾
垃圾是指在运行程序中没有任何指针指向的对象。如果不及时堆垃圾进行清理,这些垃圾会一直占用内存空间,直到程序运行结束,在这期间这些对象所占用的内存无法使用,造成极大的资源浪费。
为什么要GC
如果不 GC ,内存迟早会消耗完,导致新的对象无法分配内存,所以没有 GC 程序就可能无法正常执行。
垃圾回收行为
- 手动GC:C / C++ 需要开发人员手动进行内存的申请和回收,这样做的好处是灵活,但坏处是操作太频繁,而且对开发人员的要求较高,相对增加了开发人员的负担。
- 自动GC:Java 采用的是自动 GC 的机制。自动管理内存,无需开发人员手动分配和回收。坏处是弱化了开发人员对内存的管理,只能通过监控和调节相关参数来优化。
Java GC 主要关注的区域
- 方法区(注:方法区对应永久代或元空间,很多 JVM 没有方法区的 GC)
- 堆区
Java GC 的主要特点为:频繁收集 Young 区(年轻代),较少收集 Old 区(老年代),基本不收集 Perm 区(老年代、元空间)
对象的 finalization 机制
当垃圾回收器对垃圾对象进行垃圾回收之前,会先调用该对象的 finalize 方法,该方法在 Object 类中定义,可以被重写,主要用于在对象被回收时进行资源释放。我们通常在此方法中进行一些资源释放的操作和清理的操作,比如:File 、IO 操作的关闭、Socket 操作的关闭、数据库连接的关闭等等。
注:不要在程序中主动调用 finalize 方法,该方法仅提供给垃圾回收器调用
- 在 finalize 时,可能导致对象复活
- finalize 方法执行时间是没有保障的,它有 GC 线程决定,若不发生 GC,则不会执行。GC 时调用 finalize 方法是由单独的优先级较低一点的线程(Finalizer)来执行。执行前放在执行队列中(因为 GC 是有多个对象的 finalize 方法需要调用)
- finalize 方法还可能导致 GC 失败,所以在重写该方法时要注意执行效率等。
由于 finalize 方法,对象可能会出现如下几个状态:
- 可触及:该对象可达。(可达性算法分析)
- 可复活:对象不可达,但对象可能调用 finalize 方法后复活。(在 finalize 方法中使当前对象跟引用链中任何一个对象建立联系,就会导致对象复活。但之后再次不可用,则不会调用 finalize 方法了。finalize 方法只调用一次)
- 不可触及:finalize 方法被调用成功,且对象没有复活。只有此状态的对象才可不垃圾回收。
GC 相关算法
GC 分为两个阶段:标记阶段和清除阶段,每个阶段都有对应的算法,这些算法可以统称为垃圾回收算法。
- 标记阶段:判断对象是否存活。其对应的算法有引用计数算法和可达性分析算法
- 清除阶段:在判断对象释放存活之后,GC 接下来就会对死亡的对象进行垃圾回收,释放内存空间。目前常用的算法有,标记-清除算法(mark-sweep)、复制算法(Copying)、标记-压缩算法(mark-Compact)
引用计数算法(Reference Counting)
每个对象保存一个整型引用计数器,记录该对象被引用的情况。只要有任何一个地方引用了该对象,则该对象的计数器值 +1 ,如果不再引用了,则计数器值 -1,只要该对象的引用计数器的值为 0,则表示该对象死亡,可以被 GC 回收。
- 优点:实现简单,垃圾对象判断简单,判断效率高,回收没有延迟性。
- 缺点
- 需要独立的字段存储计数器,增加内存开销
- 任何引用的变动都需要更新计数器的值
- 无法处理循环引用(这是个致命的问题,所以目前的 JVM 中都没有使用此算法了)
可达性分析算法
可达性分析算法又叫根搜索算法或跟踪性垃圾收集,相对引用计数算法而言,可达性分析算法不仅同样具备实现简单和执行高效等特点而且可以有效的解决循环引用问题,防止内存泄漏的问题发生。Java 就是选择的可达性分析算法。
- 可达性分析算法是以根对象集合(GC Roots)为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达。
- 使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索走过的路径称为引用链(Reference Chain)
- 如果目标对象没有任何引用链相连,则对象释不可达的,可以标记为垃圾对象。只有直接或间接被根对象连接的对象才是存活对象。
GC Roots
GC Roots 包含如下几类元素:
- JVM 中引用的对象
- 如:各个线程被调用的方法中使用的参数、局部变量等。
- 本地方法栈内 JNI (本地方法)引用的对象
- 方法区中静态属性引用的对象
- 如:对象类型的静态变量
- 方法区中常量引用的对象
- 如:字符串常量池中的引用对象
- 被同步锁 synchronized 持有的对象
- JVM 内部的引用
- 如:系统类加载器、Class 对象等
- 反应 JVM 内部情况的 JMXBean 、JVMTI 中的注册回调、本地代码缓存等。
Stop The World
如果要使用可达性分析算法来判断内存是否可以回收,那么分析工作必须在一个能保障一致性的快照中进行,否则分析结果无法保证完全正确。所以在 JVM 在进行 GC 时,必须 “Stop The World” 用户线程出现停顿。
标记-清除算法(mark-sweep)
标记-清除算法就分为标记和清除两个阶段:
- 标记:收集器从根节点开始遍历,标记所有被引用的对象(注意:这里是标记的可用对象,标记的内容标记在对象头 Header 中)
- 清除:收集器从堆内存从头到尾的线性遍历,如果发现某个对象没有标记为可用对象,则将其回收。
标记-清除算法简单明了,但在进行 GC 的时候需要停止整个应用程序,而且清理出来的内存空间是不连续的,容易产生内存碎。可能导致可用空间碎片化,不能整体存放大对象。而且该算法需要经历标记-清除两步,意味着需要两次遍历对象。
标记-清除算法的清除并不是真的清除,只是将可回收的对象的地址进行记录(放在空闲列表),当有新对象来的时候,进行覆盖。
复制算法(Copying)
将内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,然后交换两个内存块的角色,最后完成垃圾回收。(这里是不是想起了什么?我们的两个幸存者区就是使用的该方式)
优点:一次遍历,更高效,复制后内存连续,没有碎片化问题。
缺点:需要两份内存空间,且任何时刻都有一个空间不使用,而且对象需要复制,当存活对象很多的情况下,复制所占用的系统资源也不少。对于存活对象不多的情况比较适用。(想想为什么在年轻代中的幸存者区使用该算法)
标记-压缩(标记-整理)算法
在年轻代中,一般只有少部分对象存活,使用复制算法,可以有效利用复制算法的优点,但在老年代中,存活对象更多,采用复制算法就会放大其缺点,所以 JVM 的开发者,在标记-清除算法的基础上改进,形成了标记-压缩算法。
标记-压缩算法也是分为两步:
- 第一阶段和标记-清除算法一样,标记所有被引用的对象。
- 将所有存活的对象压缩到内存的一端,按顺序排放,然后清理空间。
- 优点:解决了标记-清除算法的碎片化问题,消除了复制算法的内存减半的代价。
- 缺点:效率上来说还是低于复制算法,甚至低于标记-清除算法。对比标记-清除算法,标记-压缩算法有对象的移动,对应的引用地址就会涉及修改等。
标记-压缩算法的开销
- 标记(mark)阶段的开销与存活对象的数量成正比。
- 清除(sweep)阶段的开销与所管理的区域大小成正比。
- 压缩(Compact)阶段的开销与存活对象的数量成正比
分代垃圾回收
- 年轻代:区域相对老年代较小,对象生命周期短,存活率低,回收频繁。基于此特点,采用了复制算法进行垃圾回收,速度快,效率高,而且年轻代中两个幸存者区就是为了利用复制算法来划分的。
- 老年代:区域相对较大,对象存活率高,生命周期较长,回收不及年轻代频繁。基于此特点,采用的是标记-清除算法和标记-压缩算法混合使用的方式实现的垃圾回收。
增量收集算法
标记-清除、标记-压缩、复制算法,都或多或少的会有 stop the world 的出现,如果垃圾收集时间过长,应用程序会被挂起时间过长,影响用户体验。基于此情况,又诞生了增量收集算法。
如果一次性进行垃圾收集,将所有的垃圾进行处理,可能导致时间过长,所以增量收集算法就采用了垃圾收集线程和应用程序线程交替执行的思想,垃圾收集线程每次执行只收集一小片区域的内存空间,然后切换到应用程序线程执行,反复执行直到垃圾收集完成。(其本质上还是我们上面说到的垃圾收集算法,只是将垃圾收集和应用程序线程交替执行,以减少 stop the world 的时间)
- 优点:减少了 stop the world 的时间
- 缺点:交替执行线程,因为线程和线程上下文的切换的消耗,会使得垃圾回收的总成本上升,造成系统吞吐量下降。
分区算法
分区算法将堆空间划分为更小的区间,分代算法按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同区域。每一个区域都独立使用,独立回收。这样一次回收多个小空间,降低了 stop the World 的时间。
相关文章:

JVM 内存和 GC 算法
文章目录 内存布局直接内存执行引擎解释器JIT 即时编译器JIT 分类AOT 静态提前编译器(Ahead Of Time Compiler) GC什么是垃圾为什么要GC垃圾回收行为Java GC 主要关注的区域对象的 finalization 机制GC 相关算法引用计数算法(Reference Count…...

memtest86 prosite v10.6
passmark官方的memtest86 v10开始支持颗粒级别的坏内存芯片定位了,对于特定的若干种CPU和芯片组的组合,支持这项功能。 当然支持颗粒定位的site版本售价4800美金,是比较贵的。所以网络上出现了破解版的,人才真是。但是鼓励大家支…...

Springboot JSP项目如何以war、jar方式运行
文章目录 一,序二,样例代码1,代码结构2,完整代码备份 三,准备工作1. pom.xml 引入组件2. application.yml 指定jsp配置 四,war方式运行1. 修改pom.xml文件2. mvn执行打包 五,jar方式运行1. 修改…...

系统架构设计师(第二版)学习笔记----层次式架构设计理论与实践
【原文链接】系统架构设计师(第二版)学习笔记----层次式架构设计理论与实践 文章目录 一、层次式体系结构概述1.1 软件体系结构的作用1.2 常用的层次式架构图1.3 层次式体系可能存在的问题点 二、表现层框架设计2.1 MVC模式2.1.1 MVC三层模式2.1.2 MVC设…...

Python之字符串详解
目录 一、字符串1、转义字符与原始字符串2、使用%运算符进行格式化 一、字符串 在Python中,字符串属于不可变、有序序列,使用单引号、双引号、三单引号或三双引号作为定界符,并且不同的定界符之间可以互相嵌套。 ‘abc’、‘123’、‘中国’…...

《视觉SLAM十四讲》-- 概述与预备知识
文章目录 01 概述与预备知识1.1 SLAM 是什么1.1.1 基本概念1.1.2 视觉 SLAM 框架1.1.3 SLAM 问题的数学表述 1.2 实践:编程基基础1.3 课后习题 01 概述与预备知识 1.1 SLAM 是什么 1.1.1 基本概念 (1)SLAM 是 Simultaneous Localization a…...

Java8 Stream API全面解析——高效流式编程的秘诀
文章目录 什么是 Stream Api?快速入门流的操作创建流中间操作filter 过滤map 数据转换flatMap 合并流distinct 去重sorted 排序limit 限流skip 跳过peek 操作 终结操作forEach 遍历forEachOrdered 有序遍历count 统计数量min 最小值max 最大值reduce 聚合collect 收集anyMatch…...

分享一下微信小程序里怎么开店
如何在微信小程序中成功开店:从选品到运营的全方位指南 一、引言 随着微信小程序的日益普及,越来越多的人开始尝试在微信小程序中开设自己的店铺。微信小程序具有便捷、易用、即用即走等特点,使得开店门槛大大降低。本文将详细介绍如何在微…...

uniapp小程序刮刮乐抽奖
使用canvas画布画出刮刮乐要被刮的图片,使用移动清除画布。 当前代码封装为刮刮乐的组件; vue代码: <template><view class"page" v-if"merchantInfo.cdn_static"><image class"bg" :src&q…...

Qt 窗口无法移出屏幕
1 使用场景 设计一个缩进/展开widget的效果,抽屉效果。 看到实现的方法有定时器里move窗口,或是使用QPropertyAnimation。 setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint |Qt::X11BypassWindowManagerHint); 记得在移…...

java毕业设计基于springboot+vue线上教学辅助系统
项目介绍 本论文主要论述了如何使用JAVA语言开发一个线上教学辅助系统 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述线上教学辅助系统的当前背景以及系统开…...

开源 Wiki 软件 wiki.js
wiki.js简介 最强大、 可扩展的开源Wiki 软件。使用 Wiki.js 美观直观的界面让编写文档成为一种乐趣!根据 AGPL-v3 许可证发布。 官方网站:https://js.wiki/ 项目地址:https://github.com/requarks/wiki 主要特性: 随处安装&a…...

STM32基本定时器中断
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、STM32定时器的结构?1. 51定时器的结构1.1如何实现定时1s的功能? 2. stm32定时器的结构2.1 通用定时器 二、使用步骤1.开启时钟2.初始…...

学习历程_基础_精通部分_达到手搓的程度
1. 计算机网络(更新版) 1.1 计算机网络-43题 1.2 2. 操作系统(更新版) 3. ACM算法(更新版) 4. 数据库(更新版) 5. 业务开发算法(更新版) 6. 分布式类(更新版) 7. 设计模式(更新版ÿ…...

Redis中的List类型
目录 List类型的命令 lpush lpushx rpush lrange lpop rpop lindex linsert llen lrem ltrim lset 阻塞命令 阻塞命令的使用场景 1.针对一个非空的列表进行操作 2.针对一个空的列表进行操作 3.针对多个key进行操作. 内部编码 lisi类型的应用场景 存储(班级…...

3D模型格式转换工具HOOPS Exchange:如何将3D PDF转换为STEP格式?
3D CAD数据在制造、工程和设计等各个领域都扮演着重要的角色。为了促进不同软件应用程序之间的协作和互操作性,它通常以不同的格式进行交换。 HOOPS Exchange是一个强大的软件开发工具包,提供了处理和将3D CAD数据从一种格式转换为另一种格式的解决方案…...

DB-GPT介绍
DB-GPT介绍 引言DB-GPT项目简介DB-GPT架构关键特性私域问答&数据处理多数据源&可视化自动化微调Multi-Agents&Plugins多模型支持与管理隐私安全支持数据源 子模块DB-GPT-Hub微调参考文献 引言 随着数据量的不断增长和数据分析的需求日益增多,将自然语言…...

Java,面向对象,内部类
内部类的定义: 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类(InnerClass),类B则称为外部类(OuterClass)。 内部类的使用场景: 类A只在类B中使用,便可以使用内部类的方法…...

唯一ID如何生成,介绍一下目前技术领域最常使用的几种方法
纵使十面大山,又如何,无妨… 概述 唯一ID(Unique Identifier)是在计算机科学和信息技术领域中用于标识某个实体或数据的唯一标识符。生成唯一ID的方法可以根据具体需求和应用场景的不同而有所不同。以下是一些目前技术领域中常用…...

【翻译】XL-Sum: Large-Scale Multilingual Abstractive Summarization for 44 Languages
摘要 当代的关于抽象文本摘要的研究主要集中在高资源语言,比如英语,这主要是因为低/中资源语言的数据集有限。在这项工作中,我们提出了XL-Sum,这是一个包含100万篇专业注释的文章摘要对的综合多样数据集,从BBC中提取&…...

配置OpenCV
Open CV中包含很多图像处理的算法,因此学会正确使用Open CV也是人脸识别研究的一项重要工作。在 VS2017中应用Open CV,需要进行手动配置,下面给出在VS2017中配置Open CV的详细步骤。 1.下载并安装OpenCV3.4.1与VS2017的软件。 2.配置Open CV环…...

1-时间复杂度和空间复杂度
为了找到最适合当前问题而估量“算法”的评价s 时间复杂度空间复杂度执行效率:根据算法编写出的程序,执行时间越短,效率就越高占用的内存空间:不同算法编写出的程序,执行时占用的内存空间也不相同。如果实际场景中仅能…...

EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析
EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析 一 ethercatconfig.h/c文件功能预览:二 ethercatconfig.h/c 中主要函数的作用:2.1.1 ec_config_init(uint8 usetable) 和 ecx_config_init(ecx_contextt *context, uint8 usetable)ÿ…...

洗地机哪个品牌好?家用洗地机选购攻略
随着家用洗地机的普及和市场的广泛认可,进入洗地机行业的制造商也越来越多。在面对众多洗地机品牌时,消费者常常感到困惑,不知道如何选择。面对众多选择,选择有良好保障的知名洗地机品牌是明智之举。知名品牌在质量、售后服务等方…...

Java数组的定义与常用使用方法
目录 一.什么是数组 二.数组的创建及初始化 数组的创建 数组的初始化 动态初始化: 静态初始化: 【注意】 三.数组的使用 数组中元素访问 遍历数组 四.数组作为方法的参数 参数传基本数据类型 参数传数组类型(引用数据类型) 作为方法的返回…...

[计算机网络]认识“协议”
认识“协议” 文章目录 认识“协议”序列化和反序列化网络计算器引入Sock类设计协议编写服务端类启动服务端编写客户端类启动客户端程序测试 序列化和反序列化 在网络体系结构中,应用层的应用程序会产生数据,这个数据往往不是简单的一段字符串数据&…...

“Notepad++“ 官网地址
notepad官网下载地址:https://notepad-plus-plus.org/downloads/ npp.8.5.8.Installer.x64 本下载地址- https://download.csdn.net/download/namekong8/88494023 1. Fix session file data loss issue. 2. Fix Explorer context menu "Edit with Notepad…...

基于单片机的自动感应门设计
博主主页:单片机辅导设计 博主简介:专注单片机技术领域和毕业设计项目。 主要内容:毕业设计、简历模板、学习资料、技术咨询。 文章目录 主要介绍一、自动感应门设计的功能概述二、系统总体方案2.1系统的总体计划2.2元器件的介绍2.2.1单片机的…...

【密评】商用密码应用安全性评估从业人员考核题库(二十-完结)
商用密码应用安全性评估从业人员考核题库(二十-完结) 国密局给的参考题库5000道只是基础题,后续更新完5000还会继续更其他高质量题库,持续学习,共同进步。 4640 单项选择题 在测评过程中遇到的PEM编码格式,…...

Tigger绕过激活锁/屏幕锁隐藏工具,支持登入iCloud有消息通知,支持iOS12.0-14.8.1。
绕过激活锁工具Tigger可以用来帮助因为忘记自己的ID或者密码而导致iPhone/iPad无法激活的工具来绕过自己的iPhone/iPad。工具支持Windows和Mac。 工具支持的功能: 1.Hello界面两网/三网/无基带/乱码绕过,可以完美重启,支持iCloud登录、有消…...