【JVM】JVM基础教程(三)

上一章:【JVM】JVM基础教程(二)-CSDN博客

目录

运行时数据区

应用场景

程序计数器

程序计数器在运行时会出现内存溢出吗?

IDEA的debug工具查看栈帧的内容

栈帧的组成

局部变量表

关于 this 的内存存储

操作数栈

帧数据

栈内存溢出

栈的默认大小

栈内存溢出模拟

栈修改大小

IDEA中修改栈默认大小

注意事项

本地方法栈

堆内存溢出模拟

Java堆

arthas中堆内存相关的功能

堆修改大小

方法区

类的元信息

运行时常量池 

方法区溢出模拟

字符串常量池 

练习

练习

练习

静态变量的存储

直接内存

最后

Java的内存分成哪几部分?

Java内存中哪些部分会内存溢出?

JDK7和8在内存结构上的区别是什么?



运行时数据区

Java虚拟机在运行Java程序过程中管理的内存区域,称之为运行时数据区

《Java虚拟机规范》中规定了每一部分的作用

应用场景

面试

内存溢出

内存调优

程序计数器

程序计数器(Program Counter Register)也叫PC寄存器,每个线程会通过程序计数器记录当前要执行的字节码指定的地址,换句话说,它的作用是在CPU执行指令时保持跟踪,确保顺序执行指令

在加载阶段,虚拟机将字节码文件中的指令读取到内存中之后,会将原文件中的偏移量转换成内存地址。每一条字节码指令都会拥有一个内存地址

在代码执行过程中,程序计数器会记录下一行字节码指令的地址。执行完当前指令之后,虚拟机的执行引擎根据程序计数器执行下一行指令

程序计数器可以控制程序指令的进行,实现分支、跳转、异常等逻辑

在多线程执行情况下,Java虚拟机需要通过程序计数器记录CPU切换前解释执行到那一句指令并继 续解释运行。

程序计数器在运行时会出现内存溢出吗?

  • 内存溢出指的是:程序在运行过程中超出了分配给它的内存限制,导致系统无法工作,程序在使用某一块内存区域时,存放的数据需要占用的内存大小超过了虚拟机能提供的内存上限
  • 因为每个线程只存储一个固定长度的内存地址,程序计数器是不会发生内存溢出的。
  • 程序员无需堆程序计数器做任何处理
  • 内存溢出与程序计数器并没有直接关系,因为程序计数器的大小是固定的,不会由于运行时数据或指令数量的增加而改变

可能导致的混淆

  • 栈溢出:当递归调用过深或使用大量局部变量时,可能导致栈内存不足,进而引发栈溢出。
  • 堆溢出:当动态分配的内存(如使用 mallocnew 等)超过可用内存时,可能导致堆溢出。

Java虚拟机栈(JVMS:Java Virtual Machine Stack)采用栈的数据结构来管理方法调用的基本数据,先进后出(First In Last Out),每一个方法的调用使用一个栈帧(Stack Frame)来保存

IDEA的debug工具查看栈帧的内容

Java虚拟机栈随着线程的创建而创建,而回收则会在线程的销毁时进行。

由于方法可能会在不同线程中执行,每个线程都会包含一个自己的虚拟机栈


栈帧的组成

局部变量表

局部变量表的作用是在运行过程中存放所有的局部变量

编译成字节码文件时,就可以确定局部变量表的内容

栈帧中的局部变量表是一个数组,数组中每一个位置称之为槽(slot),long和double类型占两个槽(难道说,一个槽4个字节?),其他类型占用一个槽

实例方法中的序号为0的位置存放的是this,指的是当前调用方法的对象,运行时会在内存中存放实例对象 的地址

关于 this 的内存存储
  1. 栈内存:

    • 在 Java 中,方法的局部变量(包括 this 引用)会存储在栈内存中。当一个方法被调用时,会创建一个栈帧,该帧包含所有的局部变量。
    • 因此,当你在某个实例方法中使用 this 时,this 引用的地址会被存储在栈内存中的栈帧里。
  2. 堆内存:

    • 实际的对象实例(即 this 所引用的对象)则存储在堆内存中。Java 的对象是在堆上分配内存的,而栈只存储对这些对象的引用。

方法参数也会保存在局部变量表中,其顺序与方法中参数定义的顺序一致。

局部变量表保存的内容有:实例方法的this对象,方法的参数,方法体中声明的局部变量。

下列代码会在局部变量表中占用几个槽 ?

public class MyTest {
    public void test4(int k, int m){
        {
            int a = 1;
            int b = 2;
        }
        {
            int c = 1;
        }
        int i = 0;
        long j = 1;
    }
}

最初我以为是9个

  • this变量:1个
  • 方法形参:2个
  • 局部代码块:2 + 1个
  • 局部变量:1 + 2个

一共 (1 + 2 + 2 + 1 + 1 + 2) = 9个

后来用jclassdb工具查看字节码命令

画图分析一下

一开始按照顺序存放局部变量this, k, m, a, b

这个时候,JVM做了优化,第一个局部代码块结束了,发现变量a, b处于【未被使用】状态,于是乎,后面新存入局部变量会替换a, b

一开始c替换a,然后c也被优化了

所以,占用了6个槽


操作数栈

操作数栈是栈帧中虚拟机在执行指令过程中用来存放临时数据的一块区域

他是一种栈式的数据结构,如 果一条指令将一个值压入操作数栈,则后面的指令可以弹出并使用该值

帧数据

帧数据主要包含动态连接、方法出口、异常表的引用

 

当前类的字节码指令引用了其他类的属性和方法时,需要将符号引用(编号)转换成对应的运行时常量池中的内存地址

  • 动态链接就保存了编号到运行时常量池的内存地址的映射关系

  • 方法出口指的是方法在正确结束或者异常结束时,当前栈帧会被弹出,同时程序计数器(PC寄存器)应该指向上一个栈帧中的下一条指令的地址。所以在当前栈帧中,需要存储此方法出口的地址
  • 换句话说,方法出口就是一个抽象的概念,而不是具体的内存空间,是方法执行完毕后控制流的转移过程和逻辑,而不是像动态连接一样是某个特定的内存地址或者变量
  • 是当方法执行完毕时,控制权从当前方法返回到调用该方法的地方。
  • 方法出口在JVM栈帧中是通过调用约定来实现的,包括返回地址和返回值的处理。栈帧确保在方法调用结束时能够正确恢复到调用者的上下文。

异常表

异常表存放的是代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置

栈内存溢出

JVM虚拟机如果栈帧过多,占用内存超过栈内存可以分配的最大值,就会出现内存溢出

JVM栈内存溢出时会出现StackOverFlowError的错误

栈的默认大小

如果我们不指定栈的大小,JVM将创建一个默认大小的栈。大小取决于操作系统和计算机的体系结构

栈内存溢出模拟

需求: 使用递归让方法调用自身,但是不设置退出条件。 定义调用次数的变量,每一次调用让变量加1。 查看错误发生时总调用的次数。

public class MyTest {
    public static int count = 0;
    // 递归方法调用自己
    public static void recursion(){
        System.out.println(++count);
        recursion();
    }
    public static void main(String[] args) {
        recursion();
    }
}

所以一共压栈了10655个栈帧,不过每个栈帧的占存不是固定的,要考虑方法内的局部变量个数、类型,还有操作数栈大小吗,动态链接信息,返回地址,也就是方法出口等

栈修改大小

  • 要修改JVM的每个线程的栈内存大小,可以使用虚拟机参数 -Xss
  • 语法:-Xss栈大小
  • 单位:字节(默认,必须是1024的倍数)、k或者K(KB)、m或者M(MB)、g或者G(GB)

IDEA中修改栈默认大小

此时多了一个虚拟机选项

修改成1MB,每个线程都有独立的栈内存,每个线程1MB,只要不是太多递归调用,还有非常复杂的方法,其实也够用的,不够的话会发现程序在使用过程中出现了栈溢出错误,那就设置大一点呗,一般都是 512KB ~ 2MB

注意事项

1.与-Xss类似,也可以使用-XX:ThreadStackSize 调整标志来配置堆栈大小

格式为:-XX:ThreadStackSize=1024

2.HotSpot版的JVM对栈大小的最大值和最小值有要求:

比如测试如下两个参数:

        -Xss1k

        -Xss1025m

Win10_x64操作系统下的JDK8测试最小值为180k,最大值为1024m

3.局部变量过多、操作数栈深度过大也会影响栈内存的大小

一般情况下,工作中即便使用了递归进行操作,栈的深度最多也就只能到几百,不会出现栈的溢出。所以此参数可以手动指定为-Xss256k,节省内存

本地方法栈

1.JVM虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧

2.在HotSpot虚拟机中,Java虚拟机栈和本地方法栈实际上使用了同一个栈空间。本地方法栈会在栈内生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来


一般Java程序中堆内存是空间最大的一块内存区域。创建出来的对象都在于堆上。

栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享

堆内存溢出模拟

需求: 通过new关键字不停创建对象,放入集合中,模拟堆内存的溢出,观察堆溢出之后的异 常信息。

现象: 堆内存大小是有上限的,当对象一直向堆中放入对象达到上限之后,就会抛出OutOfMemory 错误。

Java堆

堆空间有三个需要关注的值:used、total、max

used指的是当前已使用的堆内存,total是JVM已经分配的可用堆内存,max是JVM可以分配的最大堆内存

随着堆中的对象增多,当total可以使用的内存即将不足时,JVM会继续分配内存给堆

如果堆内存不足,JVM就会不断的分配内存,total值会变大,total最大只能与max相等

如果不设置任何的虚拟机参数,max默认是系统内存的 1/4,也就是16g的内存条,去掉2G左右的系统占用,差不多能占用3300MB - 3700MB

total默认是系统内存的1/64,在实际引用中一般都需要设置total和max的值

java (oracle.com)

Java服务端程序开发时,建议将-Xmx和-Xms设置为相同的值,这样在程序启动之后可使用的总内存就是最大内存,而无 需向java虚拟机再次申请,减少了申请并分配内存时间上的开销,同时也不会出现内存过剩之后堆收缩的情况。

-Xmx具体设置的值与实际的应用程序运行环境有关,在《实战篇》中会给出设置方案。

arthas中堆内存相关的功能

相关博客:【JVM】JVM基础教程(一)-CSDN博客

堆内存used total max三个值可以通过dashboard命令看到。

是不是当used = max = total的时候,堆内存就溢出了呢?

不是,堆内存溢出的判断条件比较复杂,在下一章《垃圾回收器》中会详细 介绍

堆修改大小

  • 要修改堆的大小,可以使用JVM参数 -Xmx(max最大值)和 -Xms(初始的total)
  • 语法:-Xmx值 -Xms值
  • 单位:字节(默认,必须是1024的倍数)、、k或者K(KB)、m或者M(MB)、g或者G(GB)
  • 限制:Xmx必须大于 2 MB,Xms必须大于1MB

为什么arthas中显示的heap堆大小与设置的值不一样呢?

arthas中的heap堆内存使用了JMX技术中内存获取方式,这种方式与垃圾回 收器有关,计算的是可以分配对象的内存,而不是整个内存。

方法区

方法区是存放基础信息的位置,线程共享,主要包含三部分内容:

方法区(Method Area)除了存储类的元信息之外,还存放了运行时常量池

常量池中存放的是字节码中的常量池内容

类的元信息

字节码文件中通过编号查表的方式找到常量,这种常量池称为静态常量池

运行时常量池 

当常量池加载到内存中之后,可以通过内存地址快速的定位到常量池中的内容,这种常量池称为运行时常量池

方法区是《Java虚拟机规范》中设计的虚拟概念,每款Java虚拟机在实现上都各不相同。Hotspot设计如下:

  • JDK7及之前的版本将方法区存放在堆区域中的永久代空间,堆的大小由虚拟机参数来控制。
  • JDK8及之后的版本将方法区存放在元空间中,元空间位于操作系统维护的直接内存中,默认情况下只要不 超过操作系统承受的上限,可以一直分配

方法区溢出模拟

需求:通过ByteBuddy框架,动态生成字节码数据,加载到内存中。通过死循环不停地加载到方法区,观察方法区是否会出现内存溢出的情况。分别在JDK7和JDK8上运行上述代码

实验发现,JDK7上运行大概十几万次,就出现了错误。在JDK8上运行上百万次,程序依旧好好的,但是内存会直线升高。这说明JDK7和JDK8在方法区的存放上,采用了不同的设计。

  • JDK7将方法区存放在堆区域中的永久代空间,堆的大小由虚拟机参数-XX:MaxPermSize=值来控制。
  • JDK8将方法区存放在元空间中,元空间位于操作系统维护的直接内存中,默认情况下只要不超过操作系统承受 的上限,可以一直分配。可以使用-XX:MaxMetaspaceSize=值将元空间最大大小进行限制

字符串常量池 

方法区中除了类的元信息、运行常量池之外,还有一块区域叫字符串常量池(StringTable)

字符串常量池存储在代码中定义的常量字符串内容中。比如“123” 这个123就会被放入字符串常量池。

字符串常量池和运行时常量池有什么关系?

早期设计时,字符串常量池是属于运行时常量池的一部分,他们存储的位置也是一致的。后续做出了调整, 将字符串常量池和运行时常量池做了拆分。

练习

public class MyTest {
    public static void main(String[] args) {
        String a = "1";
        String b = "2";
        String c = "12";
        String d = a + b;
        System.out.println(c == d);
    }
}

流程分析大概是:

加载字符串三个常量 "1" "2" "12" 到操作数栈

创建一个StringBuilder 对象

将操作数栈顶的对象引用复制,并将两个相同的引用都放到栈上

调用 StringBuilder 的构造方法,初始化 StringBuilder 实例

加载局部变量表中索引1、2的值,也就是 "1" "2"

调用 StringBuilder 的 append 方法,将 "1" 添加到 StringBuilder 中

再次调用 append 方法,将 "2" 添加到 StringBuilder 中

调用 toString 方法,将 StringBuilder 中的内容转换为字符串

将结果("12")存储到局部变量表中的索引4(即变量 'd')

 

获取 System.out 的静态引用

加载局部变量表中索引3、4的值,也就是俩 "12"

if_acmpne 42 (+7)                                                  // 如果 'c' 和 'd' 不相等,则跳转到指令42

iconst_1                                                                         // 将值1(true)推入栈中

goto 43 (+4)                                                                 // 跳转到指令43

iconst_0                                                                         // 将值0(false)推入栈中

invokevirtual #10 <java/io/PrintStream.println : (Z)V> // 打印 boolean 值到控制台

关键点:常量池中 的两个字符串通过StringBuilder拼接后的字符串,会存入堆内存

练习

public class MyTest {
    public static void main(String[] args) {
        String a = "1";
        String b = "2";
        String c = "12";
        String d = "1" + "2";
        System.out.println(c == d);
    }
}

能很直观的看出来,并没有调用StringBuilder对象的append方法进行拼接,而是隐式了一些细节操作,这应该就是JVM做的优化了

经查询资料

具体原因

  1. String d = "1" + "2";

    • 这个表达式在编译时就能够确定其值,因此 Java 编译器会将它优化为字符串常量 "12",并存储在字符串常量池中。因此,d 实际上引用的是常量池中的 "12" 对象。
  2. String e = a + b;

    • 这里的 a 和 b 是变量,它们的值在运行时才确定。使用 + 运算符进行拼接时,Java 会创建一个新的字符串对象(通常是在堆内存中),即使拼接的结果是 "12"e 引用的也是一个新的对象。因此,e 和 c 引用的是两个不同的对象。

练习

静态变量的存储

直接内存

直接内存(Direct Memory)并不在《Java虚拟机规范》中存在,所以并不属于Java运行时的内存区域。

在 JDK 1.4 中引入了 NIO 机制,使用了直接内存

主要为了解决以下两个问题:

  1、Java堆中的对象如果不再使用要回收,回收时会影响对象的创建和使用。

  2、IO操作比如读文件,需要先把文件读入直接内存(缓冲区)再把数据复制到Java堆中。

现在直接放入直接内存即可,同时Java堆上维护直接内存的引用,减少了数据复制的开销。写文件也是类似的思路。

  • 要创建直接内存上的数据,可以使用ByteBuffer
  • 语法: ByteBuffer directBuffer = ByteBuffer.allocateDirect(size);
  • 注意事项:arthas的memory命令可以查看直接内存的大小,属性名direct

如果需要手动调整直接内存的大小,可以使用-XX:MaxDirectMemorySize=大小

单位k或K表示千字节,m或M表示兆字节,g或G表示千兆字节。

默认不设置该参数情况下,JVM 自动选择 最 大分配的大小

以下示例以不同的单位说明如何将 直接内存大小设置为 1024 KB:-XX:MaxDirectMemorySize=1m -XX:MaxDirectMemorySize=1024k -XX:MaxDirectMemorySize=104857

最后


Java的内存分成哪几部分?

程序计数器、JVM栈,本地方法栈、堆内存、方法区

Java内存中哪些部分会内存溢出?

堆、栈、方法区会内存溢出

JDK7和8在内存结构上的区别是什么?

下一章:

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

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

相关文章

如何编译安装系统settings设置应用(5.0.0-Release)

本文介绍如何在OpenHarmony 5.0.0 r版本中修改系统设置应用&#xff0c;并且编译安装到开发板上 开发环境 1.dayu200开发板 2.OpenHarmony 5.0.0r 固件 3.API12 full sdk &#xff08;如果安装full sdk过程中出现报错hvigor ERROR: Cannot find module typescript,请参考 h…

【Unity】Amplify Shader Editor

Amplify Shader Editor (ASE) Amplify Shader Editor&#xff0c;是一个功能强大的基于节点的着色器开发工具&#xff0c;允许开发者在 Unity 中轻松创建和管理复杂的 Shader。 主要功能和特点 基于节点的编辑器&#xff1a; • 提供直观的可视化界面&#xff0c;减少手写 Sh…

Github2024-12-10 Python开源项目日报 Top10

根据Github Trendings的统计,今日(2024-12-10统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10HTML项目1Rust项目1系统设计指南 创建周期:2507 天开发语言:Python协议类型:OtherStar数量:241693 个Fork数量:42010 次关注人…

1.2.3计算机软件

一个完整的计算机系统由硬件和软件组成&#xff0c;用户使用软件&#xff0c;而软件运行在硬件之上&#xff0c;软件进一步的划分为两类&#xff1a;应用软件和系统软件。普通用户通常只会跟应用软件打交道。应用软件是为了解决用户的某种特定的需求而研发出来的。除了每个人都…

ElementEye,网页分析器

介绍 我们经常使用Python写爬虫&#xff0c;爬到网页数据之后&#xff0c;就需要用beautifulSoup进行解析。因为写爬虫并不是我的主营工作&#xff0c;大多数只是用来分析一下想要的数据而已&#xff0c;所以经常会忘记beautifulSoup的用法。 同时&#xff0c;我们总是分析页面…

Qt 联合Halcon配置

文章目录 配置代码窗口绑定 配置 选择添加库 选择外部库 LIBS -LC:/Program Files/MVTec/HALCON-17.12-Progress/lib/x64-win64/ LIBS -lhalconcpp\-lhdevenginecpp\-lhalconINCLUDEPATH C:/Program Files/MVTec/HALCON-17.12-Progress/include DEPENDPATH C:/Program Fil…

图像像素如何排列?是如何存储到diocm里面?读取到内存中是如何存储?

图像像素的排列和存储在DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff0c;医学数字成像和通信&#xff09;文件中遵循特定的标准。DICOM 是一种国际标准&#xff08;ISO 12052&#xff09;&#xff0c;用于处理、存储、打印和传输医学影像信息。 …

深度学习常用损失函数介绍

均方差损失&#xff08;Mean Square Error&#xff0c;MSE&#xff09; 均方误差损失又称为二次损失、L2损失&#xff0c;常用于回归预测任务中。均方误差函数通过计算预测值和实际值之间距离&#xff08;即误差&#xff09;的平方来衡量模型优劣。即预测值和真实值越接近&…

在PowerShell下运行curl命令出现错误:Invoke-WebRequest : 无法处理参数,因为参数名称“u”具有二义性

今天在Windows 11下测试Nanamq的HTTP API&#xff0c;按照其文档输入&#xff1a; curl -i --basic -u admin:public -X GET "http://localhost:8081/api/v4/subscriptions" 结果出现二义性错误&#xff1a; 而且输入curl --help命令想看看参数说明的时候&#xff…

学者观察 | Web 3.0生态治理及其安全——北京交通大学副教授李超

导语 李超教授认为Web 3.0中无论是链上治理还是链下治理都有其优劣。链下治理机制更侧重于社区广泛参与和讨论&#xff0c;过程较为繁琐&#xff0c;但能够形成广泛的社区支持和参与&#xff0c;增强决策的合法性和接受度&#xff1b;链上治理机制通过直接在区块链上执行决策&…

React Router 6的学习

安装react-router-dom npm i react-router-dom 支持不同的路由创建 createBrowserRouter 特点 推荐使用的方式&#xff0c;基于 HTML5 的 History API。支持用户友好的 URL&#xff0c;无需 #。适用于生产环境的绝大多数场景。 适用 使用现代浏览器&#xff0c;支持 pus…

光猫开DMZ教程

本教程以移动光猫未例&#xff0c;具体操作以实际光猫为准 1、登录移动光猫管理后台 打开浏览器&#xff0c;在浏览器地址栏输入移动光猫登录管理地址192.168.1.1或者tplogin.cn 按“回车键”打开登录页面&#xff0c;然后输入路由器管理密码登录。 移动光猫登录页面 超级密…

图像的像素的存储结构:线性结构和二维数组

图像的像素的存储结构 无论是灰度图像还是RGB图像&#xff0c;当它们被读取到内存中时&#xff0c;通常也是以线性结构存储的。 这是因为计算机内存本质上是一维的地址空间&#xff0c;所有数据都是按照线性顺序存储在内存中的。 然而&#xff0c;在程序中处理这些图像时&am…

重庆轨道交通4号线寸滩地铁站自动化监测

1. 项目概述 本次项目位于重庆市轨道交通4号线中江北区寸滩站&#xff0c;轨道交通4号线是中国重庆市第八条开通运营的城市轨道交通&#xff0c;识别色为太阳橙。寸滩地处江北区中部&#xff0c;东与铁山坪街道毗邻&#xff0c;南与南岸区隔江相望&#xff0c;西与江北城中央商…

jQuery漏洞——CVE-2020-11022/CVE-2020-11023,保姆篇---春不晚

漏洞号&#xff1a;CVE-2020-11022/CVE-2020-11023 漏洞概况及影响 该类风险为应用安全缺陷类DXSS攻击&#xff0c;攻击者可以利用该漏洞注入恶意脚本代码&#xff0c;并在受害者的浏览器上执行。将导致受害者的个人信息泄露、账户被劫持、会话被劫持等安全问题。 一、漏洞版…

十五、K8s计划任务JobCronJob

K8s计划任务CronJob&Job 一、Job可以干什么 Job 控制器用于管理 Pod 对象运行一次性任务,比方说我们对数据库备份,可以直接在 k8s 上启动一个 mysqldump 备份程序,也可以启动一个 pod,这个 pod 专门用来备份用的,备份结束 pod 就可以终止了,不需要重启,而是将 Pod…

[入门一]C# webApi创建、与发布、部署、api调用

一.创建web api项目 1.1、项目创建 MVC架构的话&#xff0c;它会有view-model-control三层&#xff0c;在web api中它的前端和后端是分离的&#xff0c;所以只在项目中存在model-control两层 1.2、修改路由 打开App_Start文件夹下&#xff0c;WebApiConfig.cs ,修改路由&…

谷歌浏览器的隐私审查功能详解

随着互联网的发展&#xff0c;用户对隐私保护的关注日益增加。作为全球最受欢迎的浏览器之一&#xff0c;谷歌浏览器提供了多种隐私审查功能&#xff0c;帮助用户更好地管理和保护他们的在线隐私。本文将详细介绍如何查看Chrome浏览器的事件日志、更改标签页的打开顺序以及查看…

scala的正则表达式3

贪婪模式与非贪婪模式 1.贪婪模式 贪婪模式是正则表达式的默认行为。在这种模式下&#xff0c;量词&#xff08;如*、、?、{n,m}&#xff09;会尝试匹配尽可能多的字符。例如&#xff0c;正则表达式".*"会匹配任意数量的任意字符&#xff0c;包括空字符。 2非贪婪…

HNSW 分布式构建实践

作者&#xff1a;魏子敬 一、背景 随着大模型时代的到来&#xff0c;向量检索领域面临着前所未有的挑战。embedding 的维度和数量空前增长&#xff0c;这在工程上带来了极大的挑战。智能引擎事业部负责阿里巴巴搜推广及 AI 相关工程系统的设计和建设&#xff0c;我们在实际业务…