当前位置: 首页 > news >正文

JVM:ZGC详解(染色指针,内存管理,算法流程,分代ZGC)

1,ZGC(JDK21之前)

ZGC 的核心是一个并发垃圾收集器,所有繁重的工作都在Java 线程继续执行的同时完成。这极大地降低了垃圾收集对应用程序响应时间的影响。

  • ZGC为了支持太字节(TB)级内存,设计了基于页面(page)的分页管理(类似于G1的分区Region);
  • ZGC为了能够快速对对象进行并发标记和并发移动,对内存空间重新进行了划分,这就是ZGC中新引入的指针染色;
  • 仅支持 Linux 64 位系统,不支持 32 位平台。因此也不支持压缩指针。
  • 同时ZGC为了能更加高效地管理内存,设计了物理内存和虚拟内存两级内存管理。
  • 支持NUMA-Aware内存分配:在NUMA(非统一内存访问架构)架构下,每个处理器核心有独立管理的本地内存,访问其他核心的内存较慢。ZGC通过优先在请求线程所在处理器的本地内存上分配对象,优化了内存访问效率。

1.1,传统对象地址

【传统GC指令地址设计】在ZGC出现之前,GC信息被保存在对象头的Mark Word当中。64位JVM主要指的是JVM可以使用64位的地址空间(即64位指针),而不是每个对象的大小必须是64字节。

  • 对象头:
    • Mark Word
      • 存储对象的哈希码(如果是第一次计算哈希时会计算并缓存)。
      • GC状态:用于GC时标记对象的状态(如是否可达)。
      • 锁信息:在对象被锁定时存储锁的状态,如轻量级锁、重量级锁、偏向锁等。
    • Klass Pointer(类指针):指向该对象的类元数据,实际上是指向对象的类信息(Class对象)。该指针指向Class对象的内存地址,通过这个指针,JVM能够查找到该对象的类型信息,Class对象包含了该类的结构信息,比如类的字段、方法以及接口等。
  • 实例数据:实例数据部分存储对象的实际数据。即类中定义的实例变量(属性)。这些数据按照类的字段顺序在内存中排列。例如,如果一个类中有一个int类型和一个String类型的字段,实例数据部分就依次存储这两个字段的值。
  • 对齐填充:为了确保对象在内存中的对齐,JVM通常会对对象的内存布局进行填充。例如,32位机器上,通常要求对象的大小是8的倍数。如果对象的实际数据占用内存不满足对齐要求,JVM会插入额外的填充字节。
public class Person {int age;String name;
}

内存布局示意图(假设64位系统,按默认的内存布局):总共占用的内存可能是 8 + 8 + 4 + 8 = 28字节,但为了满足对齐要求,可能会有额外的填充字节,最终对象的大小通常会是32字节(假设JVM默认按照8字节对齐)。

1.2,ZGC对象地址

【ZGC虚拟地址空间】HotSpot虚拟机的几种收集器有不同的标记实现方案,有的把标记直接记录在对象头上(如Serial收集器),有的把标记记录在与对象相互独立的数据结构上(如G1、Shenandoah使用了一种相当于堆内存的1/64大小的,称为BitMap的结构来记录标记信息),ZGC的染色指针直接把标记信息记在引用对象的指针上(这个时候,与其说可达性分析是遍历对象图来标记对象,还不如说是遍历“引用图”来标记“引用”了),通过这四个标志位,JVM 可以从指针上直接看到对象的三色标记状态(Marked0、Marked1)、是否进入了重分配集(Remapped)、是否需要通过 finalize 方法来访问到(Finalizable)等信息。无需进行对象访问就可以获得 GC 信息,这大大提高了 GC 效率。 🚀🚀🚀

  • Remappd:对象被重新映射到新内存位置(移动过)。
  • M1:上次GC标识过。
  • M0:本次GC标识过。

注意:X86_64 处理器硬件的限制,目前 X86_64 处理器地址线只有 48 条(CPU设计时位为了降低成本,仅支持48位地址),除去 4 位染色指针,剩余可用对象地址 44 位,理论上支持 16TB 的内存。

【问题】那为啥总说ZGC最大支持内存是4TB?

【答案】目前支持的 4TB 只是人为的限制,主要是为了平衡性能、稳定性和实际需求(压根没有4TB的机器)。由于 42 位(0-41)地址最大的寻址空间就是 4TB,这就是 ZGC 一直宣称自己最大支持 4TB 内存的原因。

 6                 4 4 4  4 4                                             03                 7 6 5  2 1                                             0
+-------------------+-+----+-----------------------------------------------+
|00000000 00000000 0|0|1111|11 11111111 11111111 11111111 11111111 11111111|
+-------------------+-+----+-----------------------------------------------+
|                   | |    |
|                   | |    * 41-0 Object Offset (42-bits, 4TB address space)
|                   | |
|                   | * 45-42 Metadata Bits (4-bits)  0001 = Marked0
|                   |                                 0010 = Marked1
|                   |                                 0100 = Remapped
|                   |                                 1000 = Finalizable
|                   |
|                   * 46-46 Unused (1-bit, always zero)
|
* 63-47 Fixed (17-bits, always zero)

1.3,ZGC内存管理

【物理内存】物理内存非常直观,就是真实存在的,其大小就是插在主板内存槽上的内存条的容量大小。

【虚拟内存】虚拟地址是操作系统根据 CPU 的寻址能力,支持访问的虚拟空间,比如前些年大家使用的 32 位操作系统,对应的虚拟地址空间为 0~232 ,即 0~4GB,而我们计算机的物理内存可能只有 512MB,所以涉及物理内存和虚拟内存的映射。

【操作系统内存映射机制】虚拟内存和物理内存大小并不匹配,所以需要一个额外的机制把两者关联起来。当程序试图访问一个虚拟内存页面时,这个请求会通过操作系统来访问真正的内存。

  • 首先到页面表中查询该页是否已映射到物理页框中,并记录在页表中。
    • 如果已记录,则会通过内存管理单元(Memory Management Unit,MMU)把页码转换成页框码(frame),并加上虚拟地址提供的页内偏移量形成物理地址后去访问物理内存;
    • 如果未记录,则意味着该虚拟内存页面还没有被载入内存,这时 MMU 就会通知操作系统发生了一个页面访问错误(也称为缺页故障(page fault)),接下来系统会启动所谓的 “请页” 机制,即调用相应的系统操作函数,判断该虚拟地址是否为有效地址。
      • 如果是有效的地址,就从虚拟内存中将该地址指向的页面读入内存中的一个空闲页框中,并在页表中添加相对应的表项,最后处理器将从发生页面错误的地方重新开始运行;
      • 如果是无效的地址,则表明进程在试图访问一个不存在的虚拟地址,此时操作系统将终止此次访问。
  • 在请页成功之后,内存中已经没有空闲物理页框了,这时,系统必须启动所谓的 “交换” 机制,即调用相应的内核操作函数,在物理页框中寻找一个当前不再使用或者近期可能不会用到的页面所占据的页框。找到后,就把其中的页移出,以装载新的页面。对移出页面根据两种情况来处理:
    • 如果该页未被修改过,则删除它;
    • 如果该页曾经被修改过,则系统必须将该页写回辅存。

【问题】Java虚拟机作为一个普普通通的进程,这样随意重新定义内存中某些指针的其中几位,操作系统是否支持?处理器是否支持?

【答案】程序代码最终都要转换为机器指令流交付给处理器去执行,处理器可不会管指令流中的指针哪部分存的是标志位,哪部分才是真正的寻址地址,只会把整个指针都视作一个内存地址来对待。Solaris/SPARC平台上,硬件层面直接支持虚拟地址掩码,能够轻松忽略染色指针中的标志位,从而简化了ZGC的设计。而在x86-64平台上,没有类似的硬件支持,ZGC设计者必须依赖其他的技术手段,主要是虚拟内存映射技术,以弥补这一缺陷。

【答案】ZGC仅支持64位系统,它把64位虚拟地址空间划分为多个子空间。当创建对象时,首先在堆空间申请一个虚拟地址,该虚拟地址并不会映射到真正的物理地址。同时,ZGC 会在 M0、M1、Remapped 空间中为该对象分别申请一个虚拟地址,且三个虚拟地址都映射到同一个物理地址。ZGC 就是通过这三个视图空间的切换,来完成并发的垃圾回收。

​假如你要去 “中山一路3号” 这个地址拜访一位朋友,根据你所处城市的不同,譬如在广州或者在上海,是能够通过这个“相同的地址”定位到两个完全独立的物理位置的,这时地址与物理位置是一对多关系映射。

1.4,读屏障

当程序尝试读取一个对象时,读屏障会触发以下操作:

  • 检查指针染色:读屏障首先检查指向对象的指针的颜色信息。
  • 处理移动的对象:如果指针表示对象已经被移动(例如,在垃圾回收过程中),读屏障将确保返回对象的新位置。
  • 确保一致性:通过这种方式,ZGC 能够在并发移动对象时保持内存访问的一致性,从而减少对应用程序停顿的需要。
// 伪代码示例,展示读屏障的概念性实现
Object* read_barrier(Object* ref) {//如果对象已经被移动,返回新地址if (is_forwarded(ref)) {return get_forwarded_address(ref); // 获取对象的新地址}return ref; // 对象未移动,返回原始引用
}

读屏障可能被GC线程和业务线程触发,并且只会在访问堆内对象时触发,访问的对象位于GC Roots时不会触发,这也是扫描GC Roots时需要STW的原因。

1.5,ZGC工作流程

【初始态】在 ZGC 中,内存被划分为固定大小的页面(通常是 2MB),这些页面用于存储对象和管理内存。

【初始标记】ZGC 标记所有从 GC Root 直接可达的对象。

【并发标记&重新映射】

  • 【初次GC】GC Root开始对堆中对象进行可达性分析。
  • 【二次GC】把上次GC "并发迁移" 阶段迁移的对象指针修正指向到新分区。

【再标记】标记上一次标记过程新产生的对象。

【并发转移准备】为对象转移做一些前置准备,比如引用处理、弱引用清理和重定位集选择等。

【初始转移】迁移根节点直接引用的对象到新分区,这个阶段需要停顿所有的应用线程(STW),但由于只迁移根节点直接引用的对象,所以停顿时间很短。

【并发转移】并发迁移“并发标记”阶段标记的对象到新分区(对象引用指针未修改,仍指向旧分区)。

其实,在标记阶段存在两个地址视图M0和M1,上面的过程显示只用了一个地址视图。之所以设计成两个,是为了区别前一次标记和当前标记第二次进入并发标记阶段后,地址视图调整为M1,而非M0。

【问题】为何并发转移阶段,对象已转移至新分区后,却没有修改线程栈上实际的引用,依然指向旧分区?

【答案】因为如果此时再扫描线程栈,修改引用地址,要扫描的量太大,效率太低。刚好下一个GC周期也要进行扫描标记,可以利用扫描标记的时间,同时把对象引用修正指向到新分区,以此提升效率,减少停顿时间。

【问题】并发转移阶段对象已迁移,但引用指针仍指向旧分区,如何保证旧分区被清理后对象仍然可以访问?

【答案】由于未修改对象引用指针,为防止旧分区被清理,导致对象找不到的问题,此处引入了读屏障和转发表。

  • 转发表记录了对象从旧位置到新位置的映射关系,实现类似一个hash表,key是旧分区的位置,value是新分区的位置,此时当访问旧位置的对象时,通过转发表可以获取新位置。这样可以避免在整个堆空间中更新对象引用的开销,因为只需要更新转发表中的条目即可。
  • 读屏障的作用是在读取对象引用时,检查对象的标记状态并获取转发表中的映射关系。通过读屏障,ZGC能够在读取对象引用时,将访问重定向到新位置,以确保对象的访问仍然有效。如下图:每次读取引用时会触发一次读屏障。

1.6,ZGC性能 

不仅应用在并发转移阶段,还应用在并发标记阶段:将对象设置为已标记,传统的垃圾回收器需要进行一次内存访问,并将对象存活信息放在对象头中;而在ZGC中,只需要设置指针地址的第42-45位即可,并且因为是寄存器访问,所以速度比访问内存更快。

【ZGC触发时机】

  • 阻塞内存分配请求触发:当垃圾来不及回收,垃圾将堆占满时,会导致部分线程阻塞。应当避免出现这种触发方式。日志中关键字是“Allocation Stall”。
  • 基于分配速率的自适应算法:最主要的GC触发方式,其算法原理可简单描述为”ZGC根据近期的对象分配速率以及GC时间,计算出当内存占用达到什么阈值时触发下一次GC”。通过ZAllocationSpikeTolerance参数控制阈值大小,该参数默认2,数值越大,越早的触发GC。通过调整此参数解决了一些问题。日志中关键字是“Allocation Rate”。
  • 基于固定时间间隔:通过ZCollectionInterval控制,适合应对突增流量场景。流量平稳变化时,自适应算法可能在堆使用率达到95%以上才触发GC。流量突增时,自适应算法触发的时机可能会过晚,导致部分线程阻塞。通过调整此参数解决流量突增场景的问题,比如定时活动、秒杀等场景。日志中关键字是“Timer”。
  • 主动触发规则:类似于固定间隔规则,但时间间隔不固定,是ZGC自行算出来的时机,服务因为已经加了基于固定时间间隔的触发机制,所以通过-ZProactive参数将该功能关闭,以免GC频繁,影响服务可用性。 日志中关键字是“Proactive”。
  • 预热规则:服务刚启动时出现,一般不需要关注。日志中关键字是“Warmup”。
  • 外部触发:代码中显式调用System.gc()触发。 日志中关键字是“System.gc()”。
  • 元数据分配触发:元数据区不足时导致,一般不需要关注。 日志中关键字是“Metadata GC Threshold”。

2,分代ZGC

2.1,JDK21在ZGC上的升级

【支持分代】增加了对分代的支持,提高垃圾回收的性能。

  • JDK21之前:ZGC 的堆内存也是基于 Region 来分布,不过 ZGC 是不区分新生代老年代的。
  • JDK21之后:分代ZGC为年轻和年老的对象保留不同的世代,这将使 ZGC 能够更频繁地收集年轻对象,因为年轻对象往往在很年轻时就会死亡。

【分代的必要性】在程序运行过程中很多对象生命期较短,对这些短生命期对象进行回收,可以回收很多内存空间;剩余那部分生命期较长的对象,一般也不会被回收掉,所以对这些长生命期对象进行回收,可以回收的内存就比较有限了。不应该对所有对象都一视同仁,对于那些生命期短的对象要经常回收,获取高收益,对于那些生命期长的对象尽量不要浪费时间去回收。

  • 配置参数简单:-XX:+UseZGC -XX:+ZGenerational +Xmx 64g
  • 自动调节:
    • 不需要配置 -Xmn (年轻代、老年代动态变化)

    • 不需要配置 -XTenuringThreshold (什么时候晋升老年代动态变化)

    • 不需要配置 -XX:InitiatingHeapOccupancyPercent (G1 混合回收)

    • 不需要配置  -XX:ConcGCThreads (GC线程数动态变化)

ZGC 采用一种称为彩色指针的技术。为了避免掩码指针的开销,ZGC 采用了多重映射技术。多重映射是指将多段虚拟内存映射到同一段物理内存。 ZGC使用Java堆的3个视图(“marked0”,“marked1”,“remapped”),即3种不同“颜色”的堆指针和同一个堆的3个虚拟内存映射。因此,操作系统可能会报告 3 倍大的内存使用量。例如,对于 512 MB 的堆,报告的已提交内存可能高达 1.5 GB,不包括堆以外的内存。注意:多重映射会影响报告的使用内存,但物理上堆仍将使用 512 MB 的 RAM。这有时会导致一个有趣的效果,即进程的 RSS 看起来大于物理 RAM 的数量。

2.2,如何解决ZGC的RSS指标翻3倍的问题?

【ZGC染色指针】如果读者了解过普通ZGC,就一定了解它的颜色指针,通过虚拟内存高位的4个bit位标志当前的引用垃圾回收状态。这4个bit位中,有一位没有使用,而其它3位同一时间内只有1位为1,但是不管哪位为1,都要指向相同的物理地址,也就映射3次,最终造成普通ZGC的RSS指标(RSS统计的虚拟内存地址)翻了3倍。

【分代ZGC染色指针】分代ZGC需要更多的标记位,如果还使用muli-map的方式,第一可用内存会因为多加标记位减少;第二RSS指标可能是实际使用内存的4*4*4(每代4个指针)倍?所以分代ZGC在把虚拟内存交给操作系统的时候,需要清除标记位。这也是为啥ZGC一开始不支持分代的原因。

  • 保存在内存中的Java对象引用地址是有颜色的。
  • 读取出来处理的时候,通过 Load Barrier 将颜色去掉,之后再去寻址。
  • 存储的时候,通过 Store Barrier 将颜色恢复。

PS:Load Barrier 和 Store Barrier 是 ZGC 消耗 CPU 大的一个重要原因。

​对象的有效地址为46位,相对于64位操作系统对用户空间47位的限制,只少了1位,比起普通ZGC多了2位(64TB)。同时颜色指针放在了低位,有12位之多。load color(R):染色指针,跟读屏障有关。

【操作系统地址-无色指针地址】当JVM把分代ZGC中的虚拟地址交给操作系统使用时,会去掉12位染色指针,转换为如下的形式。所以操作系统看到的就是标准的虚拟地址(RSS不会翻倍),这个过程通过读屏障来实现。

2.3,如何在不产生额外成本的情况下去除和恢复颜色?

【ZGC读屏障】检查指针颜色是否是好的;普通ZGC在度屏障中先加载地址(rbx寄存器中的地址转换为虚拟地址)到rax寄存器,然后通过颜色指针验证地址是否有效(testq),如果不是有效地址则进入slow_path中(remap操作完成对象指针修复,转变为有效地址,转发表)。由于指针信息直接给到了操作系统,所以普通ZGC需要将三个虚拟地址映射到同一个物理地址上。

【分代ZGC读屏障】分代ZGC先加载地址到rax寄存器中,然后右移address_shift位(右移位数于GC阶段有关),然后判断CF和ZF是否都为0(ja指令的作用),如果该条件成立,则进入slow_path完成对象指针修复(并发标记阶段的指针修复)。

【address_shift操作】右移最右移除的低位为1时CF为1,否则CF为0。右移操作得到的结果为全0,那么ZF为1,否则ZF为0。由于地址右移时不会得到全0结果,所以这里ZF可以认为是一个0常量。关键要看CF,而CF的结果由address_shift所决定。

​一共4中情况,分别对应于不同的GC阶段的有效地址,有效地址的4个R位中根据当前所处阶段,只有1位为1。在每种情况中address_shift的值恰好可以把墨绿色的唯一的1移除掉(绿色右侧的移除)。由于JVM中地址是按8对齐的,对于一个有效的地址来说最小为8,所以低3位一定为0(00001000=8),本着能省就省的宗旨,低3位的0和读标记区进行了重叠。

由于在读地址的时候把指针信息删除了,所以在写的时候,就要把信息恢复,分代ZGC不得不在写屏障完成这个操作。在写入的时候,12个染色指针都需要参与。

【ZGC写操作】普通ZGC写入的时候只是保存了地址信息。

【分代ZGC写操作】分代ZGC在写入时则多做了4个操作。前两个操作合起来就是检测地址是否需要处理,如果需要处理进入slow_path中处理,这里slow_path主要做了如下操作:

  • 并行年轻代SATB 染色;

  • 并行老年代SATB 染色;

  • 并行Remember Set 染色。

后两条指令这是把地址左移,然后把颜色指针还原。由此可见,在写入上必然会有性能损耗。

相关文章:

JVM:ZGC详解(染色指针,内存管理,算法流程,分代ZGC)

1,ZGC(JDK21之前) ZGC 的核心是一个并发垃圾收集器,所有繁重的工作都在Java 线程继续执行的同时完成。这极大地降低了垃圾收集对应用程序响应时间的影响。 ZGC为了支持太字节(TB)级内存,设计了基…...

Docker常用命令大全

Docker容器相关命令: 创建并启动容器: docker run:创建一个新的容器并运行一个命令。例如:docker run -d -p 8080:80 nginx这将后台(-d)运行一个Nginx容器,并映射宿主机的8080端口到容器的80端口。 列出容器&#x…...

(12)springMVC文件的上传

SpringMVC文件上传 首先是快速搭建一个springMVC项目 新建项目mvn依赖导入添加webMoudle添加Tomcat运行环境.在配置tomcat时ApplicationContext置为"/"配置Artfact的lib配置WEB-INF配置文件(记得添加乱码过滤)配置springmvc-servlet文件&…...

在Linux系统中无网络安装Nginx并配置负载均衡

在Linux系统中无网络安装Nginx并配置负载均衡 在现代的Web开发和运维中,Nginx作为一个高性能的HTTP和反向代理服务器,被广泛应用于负载均衡、静态资源服务、SSL终端等场景。然而,在某些特殊环境下,服务器可能无法访问互联网&…...

Android车机DIY开发之软件篇(三)编译Automotive OS错误(1)

Android车机DIY开发之软件篇(三)编译Automotive OS错误(1) 问题 FAILED: out/soong/build.ninja cd “KaTeX parse error: Expected EOF, got & at position 49: …soong_build")" &̲& BUILDER"PWD/KaTeX parse error: Expected EOF, got & …...

基于网络爬虫技术的网络新闻分析【源码+文档+部署讲解】

目 录 1 绪论 1.1 论文研究背景与意义 1.2 论文研究内容 2 系统需求分析 2.1 系统需求概述 2.2 系统需求分析 2.2.1 系统功能要求 2.2.2 系统IPO图 2.2 系统非功能性需求分析 3系统概要设计 3.1 设计约束 3.1.1需求约束 3.1.2设计策略 3.1.3 技术实现 3.3 模块…...

uniapp区域滚动——上划进行分页加载数据(详细教程)

##标题 用来总结和学习,便于自己查找 文章目录 一、为什么scroll-view?          1.1 区域滚动页面滚动?          1.2 代码? 二、分页功能?          2.1 如何实现&#xff…...

机器学习(1):线性回归概念

1 线性回归基础 1.1 什么是线性 例如:汽车每小时60KM,3小时可以行使多长距离?已知汽车的速度,则汽车的行使距离只与时间唯一相关。在二元的直角坐标系中,描出这一关系的图是一条直线,所以称为线性关系。 线…...

关于编写测试用例的细枝末节

这里写目录标题 故障判别类-边界考虑示例1.0:若A>20.3且持续时间≥15ms时(判故周期为1000Hz),输出B为1,否则输出B为0。 故障判别类-不可恢复测试示例1.1:若A>20.3且持续时间≥15ms时…...

《计算机网络》课后探研题书面报告_了解PPPoE协议

PPPoE协议的工作原理与应用分析 摘 要 PPPoE(Point-to-Point Protocol over Ethernet)是一种广泛应用于宽带接入的网络协议,特别是在DSL(数字用户线路)和光纤网络中具有重要的应用价值。PPPoE结合了PPP协议的认证、加…...

Linux Centos 安装Jenkins到服务

一、前言 假设你已经下载了jenkins.war 安装了对应的jdk,下面我们来安装jenkins,以服务的形式安装。 二、安装 1)将jenkins.war拷贝到合适的位置,我的位置 /u01/jenkins/ ,位置你自己选。 2)创建系统用户…...

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题 问题描述 本人在使用zotero中的zotero one(青柠学术插件)的时候,使用插件跳转obsidian中的对应笔记,出现上图情况。(错误中提到的…...

基于考研概率论知识解读 Transformer:为何自注意力机制要除以根号 dk

Transformer自注意力机制中除以 d k \sqrt{d_k} dk​ ​深度剖析 【 Transformer 系列,故事从 d k \sqrt{d_k} dk​ ​说起】 LLM这么火,Transformer厥功甚伟,某天心血来潮~,再去看看! 它长这个样子: 深入…...

网络安全学习81天(记录)

前言: 小迪安全,81天,开始了php代码审计 思路: 内容: #知识点: 1、审计漏洞-SQL 数据库注入挖掘 1、审计思路-正则搜索&功能追踪&辅助工具 3、审计类型-常规架构&MVC 架构&三方框架 #章…...

MATLAB学习笔记-table

1.在table中叠加table table 的每一列具有固定的数据类型。如果要让表的所有单元格都可以任意填充,就得让每一列都是 cell 类型,这样表中每个单元格都是“一个元胞”。创建时可以先构造一个 空 cell 数组(大小为行数列数)&#x…...

mybatisPlus(条件构造器API)

文章目录 目录一、mybatisPlus的介绍二、mybatisPlus的基础使用配置BaseMapper的基本CURD(增删改查) 三、wrapper(条件构造器)条件构造器(wrapper)通用API基础条件判断:进阶条件判断&#xff08…...

5G+工业互联网迈入规模化发展新阶段

百度安全验证 https://blog.csdn.net/qq_25467441/article/details/145036191?sharetypeblogdetail&sharerId145036191&sharereferPC&sharesourceqq_25467441&spm1011.2480.3001.8118 好看视频-轻松有收获 产业供给加速提升。国内主流模组厂商引领全球5G模组…...

【CI/CD构建】关于不小心将springMVC注解写在service层

背景 之前写一个接口的时候没有察觉到将RequestBody这个注解带到service层了。 今天提交代码的时候,插件没有检测到这个低级错误,导致试飞构建连maven编译都过不了,maven找不到程序包org.springframework.web.bind.annotation这个包 结果…...

《鸿蒙Next ArkTS:开启人工智能应用开发高效新旅程》

在当今科技飞速发展的时代,人工智能与鸿蒙Next的结合正成为开发者们关注的焦点。利用鸿蒙Next的ArkTS语言开发高效的人工智能驱动的应用程序,为我们带来了前所未有的机遇和创新空间。 了解ArkTS语言与鸿蒙Next ArkTS是一种基于TypeScript的静态类型脚本…...

Unity 3D游戏开发从入门进阶到高级

本文精心整理了Unity3D游戏开发相关的学习资料,涵盖入门、进阶、性能优化、面试和书籍等多个维度,旨在为Unity开发者提供全方位、高含金量的学习指南.欢迎收藏。 学习社区 Unity3D开发者 这是一个专注于Unity引擎的开发者社区,汇聚了众多Un…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...