【JVM】class文件格式,JVM加载class文件流程,JVM运行时内存区域,对象分配内存流程

news2024/11/25 14:39:51

这篇文章本来只是想讲一下class文件格式,讲着讲着越讲越多。JVM这一块吧,知识比较散比较多,如果深研究下去如死扣《深入理解Java虚拟机》,这本书很深很细,全记住是不可能的,其实也没必要。趁这个机会直接把标题中的这些的主要知识点都总结一下,不会过深,也不会是太浮于表面的八股文,总结一下比较好记,也省的后面自己再忘了。

主要内容包括以下几部分:
class文件格式
JVM加载class文件流程
JVM运行时内存区域
对象分配内存流程

一,class文件格式

在idea中,一般我们直接点开target目录中的class文件,idea会帮助我们反编译后展示为java文件,那么如何观察到class文件的格式呢?

这里我们需要在idea中安装一个Jclasslib的插件,如下图所示:
在这里插入图片描述
鼠标光标停在类名上,点击view->show bytecode with jclasslib,即可查看到class文件。如下图所示:

在这里插入图片描述
class文件[注2]包含但不限于以下几个部分,

开头是著名的魔数0xCAFEBABE(如果没有特殊说明,这里及下面的进制都是16进制),CAFEBABE这个魔数是用于标识和校验这个文件是否是一个class文件的。

此外,class文件中还包括如Minor VersionMajor Version,用于标识Java版本号;

constant_pool(常量池),常量池用于存储字面量(Literal)符号引用(Symbolic References)。字面量比较接近Java语言层面的常量概念,如文本字符串、被声明为final的常量值等。而符号引用则属于编译原理方面的概念,主要包含下面几类常量[1]:
被模块导出或者开放的包(Package)
类和接口的全限定名(Fully Qualified Name)
字段的名称和描述符(Descriptor)
方法的名称和描述符()
方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)
动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)
常量池中的这些常量和符号引用,在JVM运行时会存放到方法区中的常量池中[1]。而如Fields、Methods等元数据信息,存放在方法区中。

另外,什么是符号引用呢?这就涉及到了JVM的懒加载机制[2],就是说在编译成字节码class文件后,JVM不会立即加载class文件,而是用到的时候再进行加载[注1]。这就导致了class文件在被编译出来后,无法立即被分配到内存,也就没有直接指向内存的指针。那么,我们的字节码指令,又是如何调用类、方法、成员变量的呢?

在字节码class文件中,方法、成员变量、类以全限定名(即字符串常量)的方式被引用。如下图所示,Fieldref_info,即存储指向成员变量的指针(在下图中举例的是指向字符串String s),可以看到,描述了其类的全限定名、成员变量的名字和其类型的全限定名,点击跳转到字段,会跳到字段中,即完成了通过字符串常量作为指针,跳转到字段/方法/类的功能,这就是所谓的字符引用,说白了就是把全限定名的字符串作为引用的标识。

这些符号引用一部分会在类加载阶段或者第一次使用的时候就要被转化为直接引用,这种转换成为静态解析。另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接[1]。

java虚拟机支持5种方法调用字节码指令:
invokestatic:调用静态方法
invokespecial:用于调用实例构造器()方法、私有方法和父类中的方法
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,会在运行时再确定一个实现该接口的对象
invokedynamic:先在运行时动态解析出调用带你限定符所引用的方法,然后再执行该方法。前面4条调用指令,分派逻辑都固化在Java虚拟机内部,而invokedynamic指令的分派逻辑是由用户设定的引导方法来决定的。

其中只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,Java语言里符合这个条件的方法共有静态方法、私有方法、实例构造器、父类方法4种,再加上被final修饰的方法(尽管它使用invokevirtual指令调用),这5种方法调用会在类加载的时候就可以把符号引用解析为该方法的直接引用。这些方法统称为“非虚方法”(Non-Virtual-Method),与之相反,其他方法就被称为“虚方法”[1]。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

讲完了常量池,class文件中剩下的就比较好讲了。如描述字段的表fields,描述方法的表methods,描述接口的表等等。这些就不展开讲了,如果对其他部分感兴趣,可以自己去查资料或者自己下一个jclasslib去看一看。

二,JVM加载class文件流程

在javac编译完class文件后,显而易见,如果要使用这些类,需要去加载它们(这里说的加载,指的是广义上的加载,不是loading这一步)。JVM加载class文件,分为以下几步,

在这里插入图片描述

第一步,加载(loading),《深入理解Java虚拟机》中是这么说的:*通过一个类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。在内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据的访问入口。*听起来是有点复杂,其实就是把双亲委派机制的工作内容说出来了:通过类的全限定名,获取其字节流,在堆中申请class对象的内存空间,把类的各种元数据如方法表、属性表、常量池等放入方法区。双亲委派机制就是用来加载类的,具体的可以看我上一篇文章,讲的就是双亲委派机制是如何工作的[4]。

第二步,连接(linking),这一步中分3小步。其中第一小步,验证(verification),验证文件格式(如魔数是否为CAFEBABE、主次版本号等)、验证元数据、验证字节码、验证符号引用。总之就是要验证文件格式是否符合JVM规范,看看是否是一个合格的class文件格式;第二小步,准备(preparation),给静态变量赋默认值;第三小步,解析(resolution),将常量池内的符号引用替换为直接引用,即前面提到的静态解析。

第三步,初始化(initializing),这一阶段会调用类构造器< clinit>()。

需要注意的是,< clinit>()构造方法,并不是实例对象的构造方法< init>()。《深入理解Java虚拟机》中讲:< clinit>()构造方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{})中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的。 我个人的理解就是,在loading阶段申请了这个类的class对象的内存,在initializing阶段,会通过编译器自动生成的类构造器方法来完成类class对象的初始化,即给静态变量和静态语句块中的变量赋初始值。毕竟静态变量是类的成员变量。

三,JVM运行时内存区域

前文讲了编译出的class文件结构,JVM加载class文件流程,剩下的我们聊一下JVM在运行时的内存区域的划分。

JVM内存可以划分为程序计数器(Program Counter, PC)Java虚拟机栈(Java Virtual Machine stack, JVM stack)本地方法栈(native method stacks)方法区(method area)堆(Heap)直接内存(Derict Memory)

其中程序计数器和Java虚拟机栈是线程私有的,本地方法栈、方法区、堆、直接内存都是线程公有的。

程序计数器,这一块内存区域较小,可以看作是当前线程所执行的字节码的行号指示器。

Java虚拟机栈,虚拟机栈描述的是Java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

对于执行引擎来说,在活动线程中,只有位于栈顶的方法才是运行的,只有位于栈顶的栈帧才是生效的,其被称为”当前栈帧“(Current Stack Frame),与这个栈帧所关联的方法被称为”当前方法“(Current Method)。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。

每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。

其中,局部变量表(Local Variables Table)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。

操作数栈(Operand Stack)也常被称为操作栈,它是一个后入先出(Last In First Out, LIFO)栈。当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作。

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。实际上因为有的方法进行静态解析而不是动态连接(还记得吗,能被invokestatic和invokespecial指令调用的方法和被final修饰的方法都会进行静态解析),不是所有栈帧的这个方法引用都能进行动态连接。

这么描述可能有点抽象,我们结合jclasslib来看一下

很简单的一段代码

在这里插入图片描述

对应的字节码

在这里插入图片描述

下面的LocalVariable Table为局部变量表

在这里插入图片描述

看到字节码望而生畏,这么多字节码指令记不住怎么办?字节码不需要记,需要的话查就好了(一般也用不到debug到字节码的程度吧,除了一些面试用的题…),左键单击字节码指令,再点击Show JVM Spec,即可跳转到Java虚拟机规范中对应字节码的查询结果[5]。

在这里插入图片描述

在这里插入图片描述

m方法字节码流程如下:
iconst_0,《JVM虚拟机规范》[5]中规定如下,因此iconst_0意为将整数int 0压入操作数栈

在这里插入图片描述

istore_1,注意description中的描述,istore_< n>中的n,是当前栈帧的局部变量表的索引值,istore_1是将操作数栈顶的整数类型变量,即栈顶的数0弹出操作数栈,并且赋值给局部变量表索引为1的位置,即给局部变量i赋值为0。iconst_0和istore_1结合起来,就是代码int i = 0;

在这里插入图片描述

在这里插入图片描述

iload_1,将局部变量表中索引为1的位置的数据弹出,并压入操作数栈中。

在这里插入图片描述

iinc 1 by 1,将局部变量表中索引为1位置的数+1 。注意这个操作和操作数栈没有关系。完成了i++

在这里插入图片描述

istore_2,将操作数栈中的数0弹出,赋值给局部变量表中2号索引位置。即int j = 0;

最后,返回,没有任何返回值。如果当前栈帧(m方法)下面还有其他栈帧,当前栈帧return后弹出,会由调用m方法的栈帧继续执行,成为新的当前栈帧。当然,这里m方法没有任何调用,到此,字节码结束。

有一些面试题会考i = i++和i = ++i的区别,实质上就是字节码istore_1和iinc 1 by 1顺序调换导致的。当然任何人都不会在项目中这么写代码,考这个纯粹为了考而考了…

在这里插入图片描述

Java虚拟机栈说到这里。

本地方法栈(Native Method Stacks),与虚拟机栈所发挥的作用是非常相似的,其区别是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。《Java虚拟机规范》对本地方法栈中的方法使用的语言、使用方式和数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实现它[1]。

Java堆(Java Heap),对于Java应用来说,Java堆是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作”GC堆“(Garbage Collected Heap)[1]。

从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以Java堆中经常会出现”新生代“ ”老年代“ “永久代” “Eden空间” “From Survivor空间” "To Survivor空间"等名词,在这里笔者想先说明的是这些区域划分仅仅是一部分垃圾收集器的共同特性或者说设计风格而已,而非某个Java虚拟机具体实现的固有内存布局,更不是《Java虚拟机规范》里对Java堆的进一步细致划分[1]。

需要注意的是,尽管Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB),以提升对象分配时的效率。TLAB占用伊甸区eden,默认1%。用于给小对象在多线程的情况下不用竞争eden区分配就可以申请空间,提高效率。

方法区(Method Area),线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

其中,**运行时常量池(Runtime Constant Pool)**是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

说到方法区,不得不提一下“永久代”这个概念,尤其是在JDK8之前,许多程序员习惯在HotSpot虚拟机上开发、部署程序,很多人都更愿意把方法区称呼为“永久代”(Permanent Generation),或将两者混为一谈。本质上这两者不是等价的,因为仅仅是当时的HotSpot虚拟机设计团队选择把收集器的分代设计扩展至方法区,或者说使用分代来实现方法区而已,这样使得HotSpot的垃圾收集器能够像管理Java堆一样管理这些内存,省去专门为方法区编写内存管理代码的工作。但是对于其他虚拟机实现,譬如BEAJRockit、IBM J9等来说,是不存在永久代概念的。原则上如何实现方法区属于虚拟机的实现细节,不受《Java虚拟机规范》管束,并不要求统一。但是回过头来看,当年使用永久代来实现方法区的决定并不是一个好主意,这种设计使得Java应用更容易遇到内存溢出的问题。到了JDK7的HotSpot,已经把原来存放在永久代的字符串常量池、静态变量等移出,到了JDK8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Meta Space)来代替,把JDK7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。

直接内存(Derict Memory),直接内存并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。

在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

显然,本机直接内存的分配不会收到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括物理内存、SWAP分区或者分页文件)大小以及处理器寻址空间的限制,一般服务器管理员配置虚拟机参数时,会根据实际内存去设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域综合大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

四,对象分配内存的流程

我们通常堆(Heap)是用于存储对象实例的,那么一个对象是直接就被分配到堆中吗?并不是的。但是在了解对象的分配流程之前,我们需要先了解一些内容。

栈上分配,满足栈上分配的要求如下:
线程私有小对象
无逃逸
支持标量替换

需要说明的是,栈上分配需要预先开启标量替换和逃逸分析才能使用[6],但是一般情况下,无需对栈上分配这两项设置进行更改和调优。

其中,逃逸指的是这个对象被多个线程共享,无逃逸即这个对象就在这一段代码中使用,没有return出去[6];而标量替换是说把一个对象中的字段直接分配到栈上,用于代替整个对象。

分配缓冲区(Thread Local Allocation Buffer, TLAB)(前文讲过,这里再复述一遍),Java堆中可以划分出多个线程私有的分配缓冲区,以提升对象分配时的效率。TLAB占用伊甸区eden,默认1%。用于给小对象在多线程的情况下不用竞争eden区分配就可以申请空间,提高效率。满足TLAB的条件如下:
小对象

一个对象分配内存的流程如下
如果能够栈上分配,就栈上分配;

剩下的对象,看看是否足够大,如果足够大,直接分配到老年代。

剩下的对象,看看是否满足分配到TLAB,如果可以,分配到Eden区中的TLAB。

剩下的对象,分配到Eden区。

Eden区(包括TLAB)中的对象,等到年轻代空间满了,会触发YGC对年轻代进行GC。经历了YGC后,还存活的对象,会迁移到S1区。S1区的对象经历YGC,会进入S2区;S2区的对象经历YGC,会进入S1区。当S1区和S2区的对象在经历了YGC后达到了进入老年代的要求后,就可以进入老年代。

老年代一旦触发FGC,就会开始对整个堆进行GC,存活的对象仍然留在老年代中,直到被GC回收。

在这里插入图片描述

这里解释一下关于JVM分代的一些术语,如前文所述,部分JVM实现是按照分代设计的,按GC后存活次数分为老年代,年轻代,年轻代分为Eden区(伊甸区),S1区(Survivor1区),S2区(Survivor2区)(也有的叫做S0区和S1区,都是一个意思)。笼统的讲,新生的对象会进入Eden区,经历了YGC会进入S1区,随后在S1和S2区中间来回倒腾,直到进入老年代为止。

YGC,即Young GC或者Minor GC,是年轻代触发GC后对年轻代的垃圾回收,包括Eden和S1 S2。

FGC,即Full GC或者Major GC,是老年代触发GC后对整个堆的垃圾回收。

前文说,”对象在S1和S2中间来回移动直到满足进入老年代的条件为止“。进入老年代的条件有二,满足其一即可进入老年代:

1,超过-XX:MaxTenuringThreshold指定次数(YGC)
对于常见的垃圾回收器,这个值是不同的,如

Parallel Scavenge   15
CMS    6
G1   15

2,满足动态年龄要求,即
在Survivor空间中低于或等于某年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到-XX:MaxTenuringThreshold中要求的年龄。

注1[3]:
严格来讲,JVM没有规定何时加载,但是规定了什么时候必须初始化。
new getstatic putstatic invokestatic指令,访问final变量除外
java.lang.reflect对类进行反射调用
初始化子类的时候,父类首先初始化
虚拟机启动时,被执行的主类必须初始化
动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic 的方法句柄时,该类必须初始化

注2:
一般来说,我们说Java文件编译成为class文件字节码,再由JVM的解释器(Bytecode Interpreter)逐行解释为机器码,这是Java或者说JVM能够跨平台的关键。其实Java中有一种即时编译机制JIT(Just In-Time compiler),这种机制可以将代码编译为机器码,进而不需要每次执行热点代码都需要解释执行——编译为机器码后直接执行机器码即可。
Java默认为混合模式,即混合使用解释器+热点代码编译,起始阶段采用解释执行,通过热点代码探测,检测到热点代码后,对其进行JIT即时编译,并对其进行编译执行。
-Xmixed 默认混合模式,开始时为解释执行,启动速度较快,对热点代码进行解释和编译
-Xint 使用解释模式,启动很快,执行稍慢
-Xcomp 使用纯编译模式,执行很快,启动很慢
我们通过java -version,能够看到最后显示mixed mode,即此Java为混合模式。可以通过上述VM参数改变为解释模式或者纯编译模式。

在这里插入图片描述

参考文章:
[1],深入理解Java虚拟机,周志明,第三版
[2],谈谈对Java中符号引用和引用的理解
[3],03_class_loading_linking_initializing.pdf
[4],【JVM】简述类加载器及双亲委派机制
[5],Chapter 6. The Java Virtual Machine Instruction Set
[6],jvm:优化-栈上分配

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

附录6-4 黑马优购项目-分类和购物车

目录 1 分类 1.1 接口 1.2 窗口限制 1.3 选中状态样式判断 1.4 点击左侧时右侧会到顶点 1.5 源码 2 购物车 2.1 store 2.2 tabBar徽标 2.3 滑动删除 2.4 结算 2.4.1 结算前登录 2.4.2 结算功能 2.5 触发组件事件 2.6 源码 1 分类 分类最上部是…

Flutter笔记:谈Material状态属性-为什么FlatButton等旧版按钮就废弃了

Flutter笔记 谈Material状态属性-为什么FlatButton等旧版按钮就废弃了 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this artic…

免安装SQL管理工具HeidiSQL建库如何选Collation字符校对

免安装SQL管理工具HeidiSQL 文章目录 免安装SQL管理工具HeidiSQL一、安装二、建库因此&#xff0c;通常我们选择&#xff1a; 一、安装 到官方网址&#xff1a;https://www.heidisql.com/ 下载后按不同版本安装或解压&#xff0c;运行目录中的heidisql应用程序。 该工具可以对…

「生存即赚」链接现实与游戏,打造3T平台生态

当前&#xff0c;在线角色扮演游戏&#xff08;RPG&#xff09;在区块链游戏市场中正迅速崛起&#xff0c;成为新宠。随着区块链技术的不断进步&#xff0c;众多游戏开发者纷纷将其游戏项目引入区块链领域&#xff0c;以利用这一新兴技术实现商业价值的最大化。在这一趋势中&am…

加州大学欧文分校英语中级语法专项课程02:Adjectives and Adjective Clauses 学习笔记

Adjectives and Adjective Clauses course certificate 本文是 https://www.coursera.org/learn/adjective-clauses 这门课的学习笔记。 文章目录 Adjectives and Adjective ClausesWeek 01: Adjectives and Adjective PhrasesLearning Objectives Adjectives Introduction Le…

基于Java的智慧社团综合管理系统的设计与实现(论文+源码)_kaic

摘 要 随着校园文化的不断丰富&#xff0c;大学里各种社团越来越多&#xff0c;社团活动也越来越频繁&#xff0c;社员也越来越多&#xff0c;而且大学生退社、入社比较频繁&#xff0c;社团管理就显得非常繁琐而又复杂,如果采用人工管理,对管理员来说将是一件很头疼的事情。设…

5个本地流畅运行大模型的免费工具

大家好&#xff0c;随着大型语言模型&#xff08;LLM&#xff09;驱动的聊天机器人逐渐普及&#xff0c;给人们的工作和生活带来了前所未有的便利。然而&#xff0c;这种便捷性背后潜藏着个人隐私信息被泄露的风险&#xff0c;例如AI公司会收集聊天记录和元数据来优化模型&…

【Vulhub靶场】Nginx 漏洞复现

Nginx 漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09;1、影响版本2、漏洞原理3、漏洞复现 二、Nginx 解析漏洞1、版本信息&#xff1a;2、漏洞详情3、漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09; 1、影响版本 Nginx …

mysql 数据转excel文件

mysql 数据转excel文件 缘由 为售后拉取数据&#xff0c;用navicat太墨迹了&#xff0c;用python写一个main方法跑一下&#xff1b; 1.抽取共同方法&#xff0c;封装成传入mysql&#xff0c;直接下载成excel&#xff1b; 2.写入所有sql语句&#xff0c;传入参数&#xff1b; 代…

2024-05-02 商业分析-杭州小万科技-商业模式分析

摘要: 对杭州小万科技的商业模式进行分析,以对其做出客观的评估。 杭州小万科技的资料: 杭州小万科技有限公司 - 企知道 (qizhidao.com) 杭州小万科技有限公司网站备案查询 - 天眼查 (tianyancha.com) 杭州小万科技有限公司 - 爱企查 (baidu.com) ​ 2023年年报:

Android手写自己的路由SDK

实现自己的路由框架 ​ 在较大型的Android app中常会用到组件化技术&#xff0c;针对不同的业务/基础功能对模块进行划分&#xff0c;从上到下为壳工程、业务模块、基础模块。其中业务模块依赖基础模块&#xff0c;壳工程依赖业务模块。同级的横向模块&#xff08;比如多个业务…

BeanFactory 源码浅析

BeanFactory 功能介绍 BeanFactory 是核心容器&#xff0c;负责管理 Bean 对象 BeanFactory 接口的功能只有一个 getBean() 方法BeanFactory 的实现类&#xff08;DefaultListableBeanFactory&#xff09;包含&#xff1a;控制反转、基本的依赖注入、Bean 生命周期的各种功能…

【Python】函数设计

1.联系函数的设计 2.找质数 3.找因子 4.判断水仙花数 5.斐波拉契数列递归调用&#xff0c;并用数组存储已计算过的数&#xff0c;减少重复计算 1、计算利息和本息 编写两个函数分别按单利和复利计算利息,根据本金、年利率、存款年限得到本息和和利息。调用这两个函数计算1…

【算法刷题日志】吸氧羊的StarryCoding之旅 - 贡献法计算

题目链接&#xff1a;https://www.starrycoding.com/problem/3 题目描述 吸氧羊终于注册了一个StarryCoding账号&#xff01;&#xff08;她很开心&#xff09; 但是吸氧羊忘记了它的密码&#xff0c;她想起你是计算机大师&#xff0c;于是就来请教你。 她虽然不记得密码了…

java版数据结构:深入理解栈和队列:数据结构与应用(vector,stack,queue)

目录 前言 动态数组类&#xff08;vector&#xff09; 特点&#xff1a; 应用&#xff1a; 栈&#xff08;Stack&#xff09; 栈的基础概念&#xff1a; 栈的常用方法&#xff1a; 模拟栈操作&#xff1a; 队列&#xff08;Queue&#xff09; 队列的基础概念 队列的常…

golang学习笔记(协程的基础知识)

golang的协程 协程是一种轻量级的线程&#xff0c;它可以实现并发执行的并行操作。协程是Go语言中的一个核心特性&#xff0c;它使得程序能够以并发的方式运行&#xff0c;并且非常高效。与传统的线程相比&#xff0c;协程的创建和销毁成本非常低&#xff0c;可以方便地启动大…

三维坐标点按剖面分类

一、写在前面 ①配套文件&#xff1a;根据剖面对三维坐标点&#xff08;X,Y,Z&#xff09;分类资源-CSDN文库 ②脱敏处理&#xff1a;蚀变数据已采用随机数生成覆盖 ③剖面坐标按顺序排列在“剖面坐标点.xlsx”文件中 二、3点确定空间中平面方程 原理&#xff1a; 设3点A&…

C++深度解析教程笔记2

C深度解析教程笔记2 第3课 - 进化后的 const 分析实验-C与C的const区别实验-C与C的const区别&const作用域 第4课 - 布尔类型和引用小结 本文学习自狄泰软件学院 唐佐林老师的 C深度解析教程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 第3课 - 进化后…

列转行(spark 与presto语法)

一、Presto 语法 原始数据&#xff1a; 期望数据&#xff1a; 代码&#xff1a; SELECT info, value FROM ( select 张三 as name,18 as age,男 as gender,清华 as schoolunion allselect 李四 as name,18 as age,男 as gender,清华 as school ) as a CROSS JOIN UNNEST(…

Unreal 编辑器工具 批量重命名资源

右键 - Editor Utilities - Editor Utility Blueprint&#xff0c;基类选择 Asset Action Utility 在类默认值内&#xff0c;可以添加筛选器&#xff0c;筛选指定的类型 然后新建一个函数&#xff0c;加上4个输入&#xff1a;ReplaceFrom&#xff0c;ReplaceTo&#xff0c;Add…