深入理解Java虚拟机(JVM)
JVM概述
JVM作用
java虚拟机负责装载字节码到其内部,解释/编译为对应平台上的机器码指令执行,通俗说就是将字节码转换为机器码
JVM内部构造
1、类加载部分:负责把硬盘上的字节码加载到内存中(运行时数据区)
2、运行时数据区:负责存储运行时产生的各种数据;类信息,对象信息,方法信息.....
3、执行引擎:将字节码转为机器码
4、本地方法接口:调用本地方法,例如启动线程start0();还有Object类中的hashCode()--对象的内存地址;还有文件操作中的read方法调用read0
5、垃圾回收部分
JVM类加载
类加载系统
类加载系统负责将硬盘上的字节码文件加载到JVM中,生成类的Class对象,存储在方法区。类就是一个模板。
类加载过程

1、加载
以二进制文件流进行读取,在内存中为类生成Class文件
2、链接
-
验证:检验被加载时的类是否有正确的内部结构,并和其他类协调一致
-
准备:为类的静态属性进行初始化赋值(static和final先赋值为默认值,在初始化阶段赋值为设置的指定值)
-
解析:将类中的符号引用替换为直接引用
3、初始化
初始化主要是为类的静态变量赋予正确的初始值;类加载执行完初始化阶段,才说明类加载完成了
类在哪些情况下会被加载:
-
调用类中静态成员(变量,方法)
-
new 类的对象
-
在类中执行main()
-
反射加载类 class.forName("地址")
-
子类被加载会会导致父类初始化
类在以下两种情况下不会加载:
1.类作为数组类型
Demo demo[] = new Demo[10]; //new 的数组对象不是Demo对象
2.只是访问类中的静态常量
System.out.println(Demo.P); //优化 不是加载整个类,只是获取到静态常量
类加载器
类加载器就是实际负责读取类的功能。
类加载器的分类
站在jvm的角度上,分为:引导加载器(不是用java写的是用c/c++),负责读取加载java中底层系统库;java写的类加载器
再细分类加载器可以分为:
-
启动类加载器:用c/c++语言实现负责加载java的核心类库(系统库,java.lang)
-
扩展类加载器:用java语言实现,继承ClassLoader类,加载jre下面的扩展类的jre/lib/ext子目录
-
应用程序类加载器:用java语言实现继承ClassLoader类,用来加载我们自己开发的应用程序类
JVM运行时数据区
存储运行时产生的各种数据
程序计数器
程序计数器用来记录每一个线程执行的指令位置,他的速度是最快的而且是线程私有的(每一个先线程都会有一个私有的程序计数器)此区域不会出现内存溢出(不够用),也不会出现垃圾回收
虚拟机栈
栈是运行的,解决程序方法执行,运行我们自己写的java方法
调用方法是方法入栈,结束之后出栈
一个方法就是一个栈帧,在栈帧中存储局部变量,运行结果....
虚拟机栈也是线程私有的,线程之间互相隔离
栈区域不存在垃圾回收,但是会存在内存溢出的问题
栈帧中的内容:
-
局部变量表:int a = 10;
-
操作数栈(计算过程) :a+b
-
方法返回地址
本地方法栈
本地方法栈是用来执行调用的本地方法,也是线程私有的不会存在垃圾回收,可能会出现内存溢出的问题
堆内存
堆的概述
堆的作用是用来存储java语言中产生的对象
是运行时数据区中最大的一块内存空间这个空间可以自己设置。
堆空间是所有线程共享的
堆空间是垃圾回收的重点区域,堆中没有被使用到的对象会被垃圾回收器回收掉
有可能出现内存溢出
堆内存的区域划分
堆分为:新生区(新生代/年轻代)和老年区(老年代)
新生区又分为伊甸园区和幸存者0区,幸存者1区
为什么要进行分区
可以将不同生命周期的对象存储在不同的区域,针对不同的区域采取不同的垃圾回收算法,使得垃圾回收策略更加优化

对象创建内存分配过程
新创建的对象都存储在伊甸园区
当垃圾回收时,将还被使用的对象转移至某一个幸存者区,将伊甸园区的垃圾对象进行清除
当下一次垃圾回收时,将伊甸园区存活的对象与当前正在使用的幸存者区的对象转移到另一个幸存者区(每次会空闲一个幸存者区)
当一个对象经历过15次垃圾回收后仍然存活,那么就把该对象移动到老年代
老年代比较少的进行垃圾回收,在老年代空间不足时,对老年代会进行垃圾回收
当回收后内存仍然不足时,会触发FULL GC(整堆收集 应尽量避免)
当整堆收集后仍然不顾使用那么就出现内存溢出错误--OOM
JVM调优
可以根据程序具体使用场景,对运行时数据区的各种空间大小进行调整,例如堆,方法区
方法区
主要用来存储加载的类信息
方法区的大小也是可以设置的
方法区也会进行垃圾回收,也有可能出现内存溢出
方法区的垃圾回收
方法区的垃圾回收是对类的信息进行回收
类信息如果不再被使用,类信息也可以被卸载
卸载条件:该类所产生的对象不存在了;该类的class对象也不再被使用;加载该类的类加载器也被回收了
本地方法接口
是虚拟机中专门用来调用本地方法的接口
什么是本地方法
在java中被native关键字修饰的方法就是本地方法,没有方法体,不是用java语言实现的方法,用c/c++在操作系统底层实现的方法。
例如Object中的hashCode()获取对象的内存地址;IO中读文件(输入文件 操作硬盘) read0();启动线程start0(),就是把这个线程注册到操作系统中。
java中为什么要调用本地方法
因为java属于应用层语言,有时候需要对硬件系统资源调用。
此时不方便,在一个系统资源不允许对应用层程序直接调用
那么就需要通过本地方法去调用操作硬件资源
执行引擎
执行引擎是虚拟机中核心部件之一,主要作用是将加载到虚拟中的字节码再次转换为机器码(字节码并不是系统能够直接执行的机器码)
执行引擎可以通过解释/编译两种方式,实现将字节码转换为机器码
在java程序执行的过程中涉及两次编译:
第一次是将源代码(.java)通过jdk调用编译器编译成.class文件,称为前端编译
第二次通过执行引擎将字节码文件编译为机器码称为后端编译
将字节码转换为机器码有两种方式:
解释器(解释执行):对字节码逐行进行翻译,重复性代码也是每次都要解释执行,效率低
编译器(编译执行):对某段字节码进行整体编译,然后存储起来,以后使用时就不需要在编译了,效率高;会针对执行过程中的热点代码进行编译并缓存起来。
为什么要使用解释执行和编译执行并行这样的设计?
程序开始运行时解释器可以立即发挥作用,投入使用,而编译器虽然执行效率高但是前期需要对热点代码进行跟踪和编译,需要消耗时间。
垃圾回收
什么时垃圾对象?
就是一个对象不在被任何的引用所指向,没有任何引用指向的对象,垃圾对象如果不清理,新的对象可能没有足够空间,可能会导致内存溢出
垃圾回收发展
早期c/c++这类语言,内存管理都是手动的,使用时申请,使用完手动释放;优点是对内存管理的更加精确,效率更高,缺点是增加程序员的负担,控制不好容易出事(忘了释放,误操作内存空间)
后来发展为自动回收:java,c#....都采用自动垃圾回收;优点是解放了程序员,缺点是占用了一定的内存空间(垃圾不是出现后立即回收),降低了程序员管理内存的能力。
哪些区域会出现垃圾回收
堆:对象的回收 频繁回收年轻代,较少回收老年代
方法区:类信息的卸载 整堆收集时会进行回收
垃圾回收相关算法
垃圾标记阶段算法
垃圾标记阶段就是将虚拟机中不在被任何引用指向的对象标记出来,在垃圾回收阶段就会将标记出来的对象进行回收。
引用计数算法(存在缺陷 没有被虚拟机所使用)
设计思想:在对象中维护一个计数器变量,当有引用指向对象时计数器加一,相反引用断开就减一
优点:设计思想简单容易分辨对象是否是垃圾对象
缺点:需要维护一个变量存储引用数量,频繁修改引用计数器的变量,占空间且耗时,最重要的是他无法解决循环引用问题。
可达性分析算法(根搜索算法)
设计思想:从一些可以被称为GCRoots的对象开始向下查找,只要某一个对象与GCRoots对象有联系,就可以判断对象是被使用的,与根对象引用链没有任何关系的就视为垃圾对象
哪些对象可以作为GCRoots(根对象)?
-
虚拟机栈中(被调用的方法)所使用的对象
-
类中的静态属性
-
虚拟机中使用的系统类对象
Object类中的finalize()方法
这个方法是在这个对象要在对象被回收之前虚拟机自动调用的。
在对象回收前需要执行的一些操作就可以在此方法中编写
finalize方法可以在子类中重写
finalize方法只能调用一次(第一次被判定为垃圾,要对其回收,调用finalize,对象有可能又被引用了,对象就不能被回收,当下次判定为垃圾对象时,就不会在调用finalize)
生存还是死亡?
由于finalize方法存在,被标记为垃圾的对象,也不是非死不可的,可以将对象分为三种状态:
-
可触及:被GCRoots引用的,不是垃圾对象
-
可复活的:被判定为垃圾对象,但是finalize方法还没有被调用过
-
不可触及的:被判定为垃圾的,finalize已经被调用过了
垃圾回收阶段算法
标记-复制算法
将内存分为多个较小的块,当垃圾回收时,将一个区域中存活的对象复制到另一个区域中,在另一个区域从头开始排列,清楚当前垃圾回收的区域

优点:清理之后,内存没有碎片
不足:回收时需要移动对象,所以适合小内存块,而且存活对象较少的情况
标记-清除算法
将被标记为垃圾的对象地址进行记录,后面如果分配新对象,判断垃圾对象空间是否能够存储下新的对象,如果能够存储下,用新的对象直接覆盖垃圾即可,存活对象是不发生移动的

优点:不会移动对象
不足:回收后,内存中会出现碎片
标记-压缩(整理)算法
将存活的对象会移动到内存区域的一端按顺序进行排列(压缩),清理边界以外的空间
在标记清除的基础上进行一次内存整理
优点:回收后没有内存碎片
标记-清除和标记-压缩的对比:前者不会移动存活对象,后者会移动存活对象,但是两者都适合老年代对象回收
先使用标记清除算法,当老年代空间不足时在使用标记压缩算法
垃圾回收时,根据不同分区采用不同算法;新生代:标记复制,老年代标记清除和标记压缩
垃圾回收相关概念
内存溢出与内存泄漏
内存溢出:简称OOM(OutOfMemory)简单来说就是内存不够用。
内存泄漏:系统中那些用不到的,但是又回收不到的对象
案例:单例对象,数据库连接对象,IO流,socket,这些提供close()的类,用完之后如果没有关闭,垃圾回收器不能主动回收这些对象。
内存泄漏虽然不能直接触发内存溢出但是长期有对象不能被回收也是导致内存溢出的原因之一。
Stop the World
简称STW,垃圾回收时会经历两个阶段:一是标记阶段,二是回收阶段,在标记和回收时需要我们用户线程暂停,不暂停标记和回收时可能会出现错标和漏标
垃圾回收器
概述
垃圾回收器是对垃圾回收过程的实现者,不同的虚拟机中垃圾回收器的种类也很多
垃圾回收器的分类
从线程数量上分:
-
单线程:垃圾回收线程只有一个
-
多线程:有多个垃圾回收线程
从工作模式上分:
-
独占式:垃圾回收线程执行时其他用户线程要暂停(STW)
-
并发式:垃圾回收线程和用户线程可有做到并发执行
从分区角度上分:
-
新生代
-
老年代
垃圾回收器性能指标
吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)
用户线程暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。
回收时内存开销:Java 堆区所占的内存大小
CMS回收器
开创了垃圾收集线程与用户下线程并发执行的先例,并发标记清除

初始标记:独占执行
并发标记:并发执行
重新标记:独占执行
并发清除:并发执行
G1(Garbage First)回收器
G1垃圾回收器继承了CMS垃圾收集线程和用户线程并行执行的特点,这样就减少了用户线程暂停的时间;同时将新生代和老年代的各个区域又划分成了更小的区域,对每个区域进行跟踪,优先回收垃圾多的区域,例如可以把伊甸园区可以划分成好几个小的区域。提高了回收效率和吞吐量,不在区分年轻代和老年代,可以做到对整个堆进行回收。非常适合服务端程序。
相关文章:
深入理解Java虚拟机(JVM)
JVM概述 JVM作用 java虚拟机负责装载字节码到其内部,解释/编译为对应平台上的机器码指令执行,通俗说就是将字节码转换为机器码 JVM内部构造 1、类加载部分:负责把硬盘上的字节码加载到内存中(运行时数据区) 2、运…...
笔试面试——逻辑题
1.n从1开始,每个操作可以选择对n加1或者对n加倍,若想获得整数2014,最少需要多少个操作。 2.一个池塘,养龙虾若干,请想一个办法尽量准确的估算其中有多少龙虾? 3. S先生,P先生,Q先生他们知道桌子…...
【深度学习入门实战】基于Keras的手写数字识别实战(附完整可视化分析)
本人主页:机器学习司猫白 ok,话不多说,我们进入正题吧 项目概述 本案例使用经典的MNIST手写数字数据集,通过Keras构建全连接神经网络,实现0-9数字的分类识别。文章将包含: 关键概念图解完整实现代码训练过程可视化模型效果深度分析环境准备 import numpy as np impo…...
软考高级《系统架构设计师》知识点(一)
计算机硬件 校验码 码距:就单个编码A:00而言,其码距为1,因为其只需要改变一位就变成另一个编码。在两个编码中,从A码到B码转换所需要改变的位数称为码距,如A:00要转换为B:11,码距为2。一般来说,…...
用大模型学大模型01-制定学习计划
提示词:我想学习大模型,需要AI制定一个完整的学习计划,并给出学习路径和学习资料。以教科书目录的方式给出学习路线 第1章:数学与编程基础(4-6周) 1.1 数学基础 线性代数(矩阵运算、特征值分…...
lvs的DR模式
基于Linux的负载均衡集群软件 LVS 全称为Linux Virtual Server,是一款开源的四层(传输层)负载均衡软件 Nginx 支持四层和七层(应用层)负载均衡 HAProxy 和Nginx一样,也可同时支持四层和七层(应用层)负载均衡 基于Linux的高可用集群软件 Keepalived Keepalived是Linux…...
mysql读写分离与proxysql的结合
上一篇文章介绍了mysql如何设置成主从复制模式,而主从复制的目的,是为了读写分离。 读写分离,拿spring boot项目来说,可以有2种方式: 1)设置2个数据源,读和写分开使用 2)使用中间件…...
【C++学习篇】C++11第二期学习
目录 1. 可变参数模板 1.1 基本语法及原理 1.2 包扩展 1.3empalce系列接⼝ 2. lamba 2.1 lambda的语法表达式 2.2 捕捉列表 2.3 lamba的原理 1. 可变参数模板 1.1 基本语法及原理 1. C11⽀持可变参数模板,也就是说⽀持可变数量参数的函数模板和类模板&…...
TextWebSocketHandler 和 @ServerEndpoint 各自实现 WebSocket 服务器
TextWebSocketHandler 和 ServerEndpoint 都可以用于实现 WebSocket 服务器,但它们属于不同的技术栈,使用方式和功能有一些区别。以下是它们的对比: 1. 技术栈对比 特性TextWebSocketHandler (Spring)ServerEndpoint (Java EE/JSR-356)所属框…...
【C++高并发服务器WebServer】-18:事件处理模式与线程池
本文目录 一、事件处理模式1.1 Reactor模式1.2 Proactor模式1.3 同步IO模拟Proactor模式 二、线程池 一、事件处理模式 服务器程序通常需要处理三类事件:I/O事件、信号、定时事件。 对应的有两种高效的事件处理模式:Reactor和Proactor,同步…...
23种设计模式的定义和应用场景-02-结构型模式-C#代码
23种设计模式的定义和应用场景: 1. 创建型模式(共5种): 单例模式(Singleton)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)、建造者模式…...
数据脱敏方案总结
什么是数据脱敏 数据脱敏的定义 数据脱敏百度百科中是这样定义的: 数据脱敏,指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。这样就可以在开发、测试和其它非生产环境以及外包环境中安全地使用脱敏后的真实数据集…...
自然语言处理NLP入门 -- 第二节预处理文本数据
在自然语言处理(NLP)中,数据的质量直接影响模型的表现。文本预处理的目标是清理和标准化文本数据,使其适合机器学习或深度学习模型处理。本章介绍几种常见的文本预处理方法,并通过 Python 代码进行示例。 2.1 文本清理…...
02.10 TCP之文件传输
1.思维导图 2.作业 服务器代码: #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> …...
基于STM32的ADS1230驱动例程
自己在练手项目中用到了ADS1230,根据芯片手册自写的驱动代码,已测可用,希望对将要用到ADS1230芯片的人有所帮助。 芯片:STM32系列任意芯片、ADS1230 环境:使用STM32CubeMX配置引脚、KEIL 部分电路: 代码…...
Bro想要玩github api
Bro想要在vscode 和 rest client插件的帮助下,修改我的github个人信息 ### 先安装REST client插件 ### 文件名test-github.http ### bro需要自己在github develop setting 获得token ### ref link: https://docs.github.com/en/authentication/keeping-your-accoun…...
idea插件开发,如何获取idea设置的系统语言
手打不易,如果转摘,请注明出处! 注明原文:https://zhangxiaofan.blog.csdn.net/article/details/145578160 版本要求 大于 2024.3 错误用法 网上有的说使用:UIUtil com.intellij.util.ui.UIUtil 代码示例…...
怎麼使用靜態住宅IP進行多社媒帳號管理
隨著社交媒體平臺的多樣化,很多人發現一個社媒帳號已經無法滿足需求。以下是幾個常見場景: 企業需求:企業可能需要在不同平臺上運營多個品牌帳號,為每個市場地區單獨設立帳號。個人需求:一些自由職業者或內容創作者可…...
InfiniBand与IP over InfiniBand(IPOIB):实现高性能网络通信的底层机制
在现代高性能计算(HPC)和数据中心环境中,网络通信的效率和性能至关重要。InfiniBand(IB)作为一种高性能的串行计算机总线架构,以其低延迟、高带宽和高可靠性而广泛应用于集群计算和数据中心。IP over InfiniBand(IPOIB)则是在InfiniBand网络上实现IP协议的一种方式,它…...
掌握 PHP 单例模式:构建更高效的应用
在 PHP 应用开发中,资源的高效管理至关重要。单例模式是一种能够帮助我们实现这一目标的设计模式。本文将深入探讨单例模式的概念、工作原理以及在 PHP 项目中何时应该(或不应该)使用它。 什么是单例模式? 单例模式是一种设计模…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...
[拓扑优化] 1.概述
常见的拓扑优化方法有:均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有:有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...
MeanFlow:何凯明新作,单步去噪图像生成新SOTA
1.简介 这篇文章介绍了一种名为MeanFlow的新型生成模型框架,旨在通过单步生成过程高效地将先验分布转换为数据分布。文章的核心创新在于引入了平均速度的概念,这一概念的引入使得模型能够通过单次函数评估完成从先验分布到数据分布的转换,显…...
React、Git、计网、发展趋势等内容——前端面试宝典(字节、小红书和美团)
React React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么,Fiber架构,面试向面试官介绍,详细解释 用户: React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么,Fiber架构,面试向面试官介绍&#x…...
