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

这篇文章本来只是想讲一下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:优化-栈上分配

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

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

相关文章

RK3568平台(基础篇)linux错误码

一.概述 linux应用程序开发过程中&#xff0c;经常会遇到一些错误信息的返回&#xff0c;存在的可能性有&#xff0c;参数有误、非法访问、系统资源限制、设备/文件不存在、访问权限限制等等。对于这类错误&#xff0c;可以通过perror函数输出具体描述&#xff0c;或者通过str…

nacos-server-1.2.1启动

1、双击startup.cmd 2、启动日志 3、访问http://192.168.26.210:8848/nacos/index.html 4、登录 用户名&#xff1a;nacos 密码&#xff1a;nacos

掌握JavaScript面向对象编程核心密码:深入解析JavaScript面向对象机制对象概念、原型模式与继承策略全面指南,高效创建高质量、可维护代码

ECMAScript&#xff08;简称ES&#xff0c;是JavaScript的标准规范&#xff09;支持面向对象编程&#xff0c;通过构造函数模拟类&#xff0c;原型链实现继承&#xff0c;以及ES6引入的class语法糖简化面向对象开发。对象可通过构造函数创建&#xff0c;使用原型链共享方法和属…

【云原生】Docker 的网络通信

Docker 的网络通信 1.Docker 容器网络通信的基本原理1.1 查看 Docker 容器网络1.2 宿主机与 Docker 容器建立网络通信的过程 2.使用命令查看 Docker 的网络配置信息3.Docker 的 4 种网络通信模式3.1 bridge 模式3.2 host 模式3.3 container 模式3.4 none 模式 4.容器间的通信4.…

小白如何搭建git

1、安装git 在Windows上安装git&#xff1a; 关注微信公众号“机器人学”回复 “搭建git” 利用百度云网盘下载安装包&#xff0c;建议下载如下版本的否则可能会出现错误。 安装完成后&#xff0c;在开始菜单里Git->git bash&#xff0c;弹出命令窗说明git安装成功。 鼠标右…

【Python项目】基于DJANGO的【基于语音识别的智能垃圾分类系统】

技术简介&#xff1a;使用Python技术、DJANGO框架、MYSQL数据库等实现。 系统简介&#xff1a;用户们可以在系统上面录入自己的个人信息&#xff0c;录入后还可以对信息进行修改&#xff0c;网站可以对用户上传的音频文件进行识别&#xff0c;然后进行垃圾分类。 背景&#xf…

【学习AI-相关路程-工具使用-自我学习-NVIDIA-cuda-工具安装 (1)】

【学习AI-相关路程-工具使用-自我学习-NVIDIA-cuda &#xff08;1&#xff09;】 1、前言2、环境配置1、对于jetson orin nx 的cuda环境2、对于Ubuntu 20.04下cuda环境 3、自我总结-安装流程1、在ubuntu下&#xff0c;如果想使用cuda平台&#xff0c;应该注意什么 和 都安装什么…

BGE向量模型架构和训练细节

模型论文&#xff1a;https://arxiv.org/pdf/2309.07597 模型数据&#xff1a;https://data.baai.ac.cn/details/BAAI-MTP 训练数据 由无标签数据和有标签数据组成。 无标签数据使用了悟道等数据集&#xff0c;有标签数据使用了dureader等数据集。 都是文本对&#xff0c;对于…

rust使用Atomic创建全局变量和使用

Mutex用起来简单&#xff0c;但是无法并发读&#xff0c;RwLock可以并发读&#xff0c;但是使用场景较为受限且性能不够&#xff0c;那么有没有一种全能性选手呢&#xff1f; 欢迎我们的Atomic闪亮登场。 从 Rust1.34 版本后&#xff0c;就正式支持原子类型。原子指的是一系列…

【redis】Redis数据类型(五)ZSet类型

目录 类型介绍特点补充 使用场景 Zset类型数据结构ziplist&#xff1a;压缩列表&#xff08;参考之前的文章&#xff09;skiplist&#xff1a;跳表解析 面试题&#xff1a;MySQL索引为什么用B树而不用跳表区别总结 常用命令ZADD示例 ZREM示例 ZCARD示例 ZCOUNT示例 ZSCORE示例 …

在线OJ——链表经典例题详解

引言&#xff1a;本篇博客详细讲解了关于链表的三个经典例题&#xff0c;分别是&#xff1a;环形链表&#xff08;简单&#xff09;&#xff0c;环形链表Ⅱ&#xff08;中等&#xff09;&#xff0c;随机链表的复制&#xff08;中等&#xff09;。当你能毫无压力地听懂和成功地…

单调栈|496.下一个更大元素I

力扣题目链接 class Solution { public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {stack<int> st;vector<int> result(nums1.size(), -1);if (nums1.size() 0) return result;unordered_map<int, …

去哪儿网机票服务请求体bella值逆向

作者声明&#xff1a;文章仅供学习交流与参考&#xff01;严禁用于任何商业与非法用途&#xff01;否则由此产生的一切后果均与作者无关&#xff01;如有侵权&#xff0c;请联系作者本人进行删除&#xff01; 一、加密定位 直接全局搜索bella&#xff0c;在可疑的地方下断&…

pandas学习笔记12

缺失数据处理 其实在很多时候&#xff0c;人们往往不愿意过多透露自己的信息。假如您正在对用户的产品体验做调查&#xff0c;在这个过程中您会发现&#xff0c;一些用户很乐意分享自己使用产品的体验&#xff0c;但他是不愿意透露自己的姓名和联系方式&#xff1b; 还有一些用…

使用C#和NMODBUS快速搭建MODBUS从站模拟器

MODBUS是使用广泛的协议&#xff0c;通讯测试时进行有使用。Modbus通讯分为主站和从站&#xff0c;使用RS485通讯时同一个网络内只能有一个主站&#xff0c;多个从站。使用TCP通讯时没有这方面的限制&#xff0c;可以同时支持多个主站的通讯读写。 开发测试时有各种复杂的需求&…

05 - 步骤 JSON output

简介 JSON Output 步骤用于将 Kettle 中的行流数据写出到 JSON 格式的文件或流中。它允许用户将 Kettle 中处理过的数据以 JSON 格式进行输出&#xff0c;适用于各种数据处理和交换场景。 什么是行流数据&#xff1f; preview data 中的每一个字段都是一个行流数据 使用 场…

54.HarmonyOS鸿蒙系统 App(ArkTS)tcp socket套接字网络连接收发测试

工程代码https://download.csdn.net/download/txwtech/89258409?spm1001.2014.3001.5501 54.HarmonyOS鸿蒙系统 App(ArkTS)tcp socket套接字网络连接收发测试 import socket from ohos.net.socket; import process from ohos.process; import wifiManager from ohos.wifiMana…

ton-http-api安装部署

1、拉取github代码 mkdir /data git clone https://github.com/toncenter/ton-http-api.git cd ton-http-api2、创建环境变量 ./configure.py cat .env TON_API_CACHE_ENABLED0 TON_API_CACHE_REDIS_ENDPOINTcache_redis TON_API_CACHE_REDIS_PORT6379 TON_API_CACHE_REDIS_T…

前后端分离实践:使用 React 和 Express 搭建完整登录注册流程

文章目录 概要整体架构流程技术名词解释ReactExpressReact RouterAnt Design 技术细节前端设计后端逻辑数据交互 小结 概要 本项目是一个基于React和Express的简单登录注册系统。通过前后端分离的方式&#xff0c;实现了用户的注册、登录和查看用户列表等功能。前端使用React框…

HCIP第二节

OSPF&#xff1a;开放式最短路径协议&#xff08;属于IGP-内部网关路由协议&#xff09; 优点&#xff1a;相比与静态可以实时收敛 更新方式&#xff1a;触发更新&#xff1a;224.0.0.5/6 周期更新&#xff1a;30min 在华为设备欸中&#xff0c;默认ospf优先级是10&#…