【JVM】从零开始深度解析JVM

在这里插入图片描述

本篇博客给大家带来的是JVM的知识点, 重点在类加载和垃圾回收机制上.
🐎文章专栏: JavaEE初阶
🚀若有问题 评论区见
欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅🚀

  • 要开心
    • 要快乐
      • 顺便进步
  • 1. JVM简介
    • 1.1 HotSpot VM
    • 1.2 Taobao JVM(国产研发)
    • 1.3 JDK JRE JVM三者关系(经典面试题)
  • 2. JVM 运行流程
  • ★3. JVM运行时数据区(内存区域)
    • 3.1 堆(线程共享)
    • 3.2 Java虚拟机栈(线程私有)/本地方法栈
    • 3.3 程序计数器(线程私有)
    • 3.4 元数据区(方法区)
  • ★4. JVM类加载
    • 4.1 类加载过程
      • 4.1.1 加载
      • 4.1.2 验证
      • 4.1.3 准备
      • 4.1.4 解析
      • 4.1.5 初始化
    • 4.2 双亲委派模型
      • 4.2.1 什么是双亲委派模型?
      • 4.2.2 双亲委派模型的工作过程.
      • 4.3 双亲委派模型优点
      • 4.4 破坏双亲委派模型
  • 5. 垃圾回收机制
    • 5.1 死亡对象判断算法.
      • 5.1.1 引用计数算法.
      • 5.1.2 可达性分析算法
    • 5.2 垃圾回收算法
      • 5.2.1 标记-清除算法
      • 5.2.2 复制算法
      • 5.2.3 标记-整理算法
      • 5.2.4 分代算法

要开心

要快乐

顺便进步

1. JVM简介

JVM 是 Java Virtual Machine 的简称,意为 Java虚拟机。
虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。
常见的虚拟机:JVM、VMwave、Virtual Box.

JVM 和其他两个虚拟机的区别:
① VMwave与VirtualBox是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器;
② JVM则是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都进行了裁剪.

JVM 是一台被定制过的现实当中不存在的计算机.

1.1 HotSpot VM


HotSpot最初由一家名为“Longview Technologies”的小公司设计;

1997年,此公司被Sun收购;2009年,Sun公司被甲骨文收购。

JDK1.3时,HotSpot VM成为默认虚拟机

目前 HotSpot 占用绝对的市场地位,称霸武林。

不管是现在仍在广泛使用JDK6,还是使用比较多的JDK8中,默认的虚拟机都是HotSpot;

Sun/Oracle JDK和OpenJDK的默认虚拟机。从服务器、桌面到移动端、嵌入式都有应用。

名称中的HotSpot指的就是它的热点代码探测技术。它能通过计数器找到最具编译价值的代码,触发即时编译(JIT)或栈上替换;通过编译器与解释器协同工作,在最优化的程序响应时间与最佳执行性能中取得平衡。

1.2 Taobao JVM(国产研发)


阿里是国内使用Java最强大的公司,覆盖云计算、金融、物流、电商等众多领域,需要解决高并发、高可用、分布式的复合问题。有大量的开源产品。
基于OpenJDK 开发了自己的定制版本AlibabaJDK,简称AJDK。是整个阿里JAVA体系的基石;
基于OpenJDK HotSpot JVM发布的国内第一个优化、深度定制且开源的高性能服务器版Java虚拟机,
它具有以下特点(了解):


1. 创新的GCIH(GC invisible heap)技术实现了off-heap,即将生命周期较长的Java对象从heap中移到heap之外,并且GC不能管理GCIH内部的Java对象,以此达到降低GC的回收评率和提升GC的回收效率的目的;

2. GCIH中的对象还能够在多个Java虚拟机进程中实现共享;

3. 使用crc32指令实现JVM intrinsic降低JNI的调用开销;

4. PMU hardware的Java profiling tool和诊断协助功能;

5. 针对大数据场景的ZenGC。

taobaoJVM应用在阿里产品上性能高,硬件严重依赖intel的cpu,损失了兼容性,但提高了性能,目前已经在淘宝、天猫上线,把Oracle官方JVM版本全部替换了.

1.3 JDK JRE JVM三者关系(经典面试题)

JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译器 javac 与自带的调试工具Jconsole、jstack等.

JRE(Java Runtime Environment):Java运行时环境,包含了JVM,Java基础类库。是使用Java语言编写程序运行的所需环境.

JVM:Java虚拟机,运行Java代码.

在这里插入图片描述


2. JVM 运行流程


JVM 是 Java 运行的基础,也是实现一次编译到处执行的关键,那么 JVM 是如何执行的呢?

Java程序在执行之前先要把 java 代码转换成字节码(class文件),JVM 首先需要把字节码通过一定的方式类加载器(ClassLoader) 把文件加载到内存中 运行时数据区(Runtime Data Area) ,而字节码文件是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine)将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface) 来实现整个程序的功能,这就是这4个主要组成部分的职责与功能。

在这里插入图片描述

JVM 主要通过分为以下 4 个部分,来执行 Java 程序的,它们分别是:

1. 类加载器(ClassLoader)
2. 运行时数据区(Runtime Data Area)
3. 执行引擎(Execution Engine)
4. 本地库接口(Native Interface)

★3. JVM运行时数据区(内存区域)

JVM 运行时数据区域也叫内存布局,但需要注意的是它和 Java 内存模型((Java Memory Model,简
称 JMM)完全不同,属于完全不同的两个概念,它由以下 五 大部分组成:

在这里插入图片描述

3.1 堆(线程共享)

代码中 new 出来的对象,都是在堆里, 对象中持有的非静态成员变量,也在堆里.

堆里面分为两个区域:新生代和老生代,新生代放新建的对象,当经过一定 GC 次数之后还存活的对象会放入老生代。新生代还有 3 个区域:一个 Endn + 两个 Survivor(S0/S1).

垃圾回收的时候会将 Endn 中存活的对象放到一个未使用的 Survivor 中,并把当前的 Endn 和正在使用的 Survivor 清除掉.

3.2 Java虚拟机栈(线程私有)/本地方法栈


Java 虚拟机栈的作用:Java 虚拟机栈的生命周期和线程相同,Java 虚拟机栈描述的是 Java 方法执行的内存模型: 每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表操作数栈动态链接方法出口等信息。咱们常说的堆内存、栈内存中,栈内存指的就是虚拟机栈.

1. 局部变量表: 存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的空间是完全确定的,在执行期间不会改变局部变量表大小. 简单来说就是存放方法参数和局部变量.

2. 操作栈:每个方法会生成一个先进后出的操作栈.

3. 动态链接:指向运行时常量池的方法引用.

4. 方法返回地址:PC 寄存器的地址.

什么是线程私有?

由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时刻,一个处理器(多核处理器则指的是一个内核)都只会执行一条线程中的指令。因此为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存储。我们就把类似这类区域称之为"线程私有"的内存。

本地方法栈和虚拟机栈类似,只不过 Java 虚拟机栈是给 JVM 使用的,而本地方法栈是给本地方法使用的。

此处谈到的“堆” "栈"和数据结构中的"堆” "栈"是不同的! 面试中如果被问到堆和栈, 一定要反问面试官,搞清楚问的是哪个堆,哪个栈?

3.3 程序计数器(线程私有)


程序计数器的作用:用来记录当前线程执行的行号的. 也是用来存储下一条要执行的 java 指令的地址.


如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,这个计数器值为空.



3.4 元数据区(方法区)


方法区的作用:用来存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据的.

运行时常量池是方法区的一部分,存放字面量与符号引用.

字面量 : 字符串(JDK 8 移动到堆中) 、final常量、基本数据类型的值.

符号引用 : 类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符.

★4. JVM类加载


类加载指的是, java 进程运行的时候,需要把 .class 文件从硬盘读取到内存,并进行一系列的校验解析的过程.

4.1 类加载过程


整个 JVM 执行的流程中,和程序员关系最密切的就是类加载的过程了,所以接下来我们来看下类加载的执行流程。

对于一个类来说,它的生命周期是这样的:

前 5 步是固定的顺序并且也是类加载的过程,其中中间的 3 步我们都属于连接,所以对于类加载来说总共分为以下几个步骤:
1. 加载
2. 连接
a. 验证
b. 准备
c. 解析
3. 初始化

4.1.1 加载


“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段,它和类加载Class Loading 是不同的,一个是加载 Loading 另一个是类加载 Class Loading,不要把二者搞混了。


在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流.
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构.
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口.

4.1.2 验证


验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全.
简而言之,就是确保当前读到的文件内容是合法的.

文件格式验证
字节码验证
符号引用验证…

4.1.3 准备


准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。

比如此时有这样一行代码:

public static int value = 888;

它是初始化 value 的 int 值为 0,而非 888.

4.1.4 解析


解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

在硬盘或者文件中不存在地址这样的概念, 虽然没有地址但是可以引入一个偏移量这样的概念来记录字符串常量的位置. 此时的偏移量就可以认为是符号引用.

4.1.5 初始化


初始化阶段,Java 虚拟机真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。初始化阶段就是执行类构造器方法的过程.

简单总结:
加载: 找到 .class 文件,并且读文件内容.

验证: 校验 .class 文件的格式是否符合 JVM 规范要求.

准备: 给类对象分配内存(此时内存空间是全0的 =>类的静态成员也就是全 0的值,即 int 默认值为0).

解析: 针对类中的字符串常量进行处理.

初始化: 把类对象的各个部分的属性进行赋值填充 =>触发对父类的加载,初始化静态成员,执行静态代码块.

4.2 双亲委派模型


提到类加载机制,不得不提的一个概念就是“双亲委派模型”。

站在 Java 虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap
ClassLoader),这个类加载器使用 C++ 语言实现,是虚拟机自身的一部分;另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader.

站在 Java 开发人员的角度来看,类加载器就应当划分得更细致一 些。自 JDK 1.2 以来,Java 一直保持着三层类加载器、双亲委派的类加载架构器。

4.2.1 什么是双亲委派模型?


如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载.

在这里插入图片描述

BootstrapClassLoader
负责查找标准库的目录.

ExtensionClassLoader
负责查找扩展库的目录.

ApplicationClassLoader
负责查找当前项目的代码目录
以及第三方库的目录.

4.2.2 双亲委派模型的工作过程.


1.从 ApplicationClassLoader 作为入口,先开始工作.

2. ApplicationClassLoader 不会立即搜索自己负责的目录,而会把搜索的任务交给自己的父亲:

3.代码就进入到 ExtensionClassLoader 范畴了ExtensionClassLoader 也不会立即搜索自己负责的目录也要把搜索的任务交给自己的父亲.

4.代码就进入到 BootstrapClassLoader 范畴了BootstrapClassLoader 也不想立即搜索自己负责的目录也要把搜索的任务交给自己的父亲.

5.BootstrapClassLoader 发现自己没有父亲才会真正搜索负责的目录 (标准库目录)通过全限定类名,尝试在标准库目录中找到符合要求的 .class 文件. 如果找到了,接下来就直接进入到打开文件/读文件等流程中如果没找到,回到孩子这一辈的类加载器中,继续尝试加载.

6. ExtensionClassLoader 收到父亲交回给他的任务之后,自己进行搜索负责目录(扩展库的目录)…

7. ApplicationClassLoader 收到父亲交回给他的任务之后自己进行搜索负责的目录(当前项目目录/第三方库目录)…

4.3 双亲委派模型优点


1. 避免重复加载类:比如 A 类和 B 类都有一个父类 C 类,那么当 A 启动时就会将 C 类加载起来,那么在 B 类进行加载时就不需要在重复加载 C 类了.

2. 安全性:使用双亲委派模型也可以保证了 Java 的核心 API 不被篡改,如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object
类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类,而有些 Object 类又是用户自己提供的因此安全性就不能得到保证了.

4.4 破坏双亲委派模型


上述这一系列规则,只是 JM 自带的类加载器遵守的默认规则. 如果咱们自己写类加载器,也可以打破上述规则比如自己写类加载器, 指定这个加载器就在某个目录中尝试加载. 此时如果类加载器的 parent 不去和已有的这些类加载器连到一起, 此时就是独立的,不涉及到双亲委派了.


5. 垃圾回收机制

堆是垃圾回收的主战场.

Java堆中存放着几乎所有的对象实例,垃圾回收器在对堆进行垃圾回收前, 首先要判断这些对象哪些还存活,哪些已经"死去". 判断对象是否已"死"有如下几种算法.

在 Java 中,所有的对象都是要存在内存中的(也可以说内存中存储的是一个个对象),因此我们将内存回收,也可以叫做死亡对象的回收。


5.1 死亡对象判断算法.

在 Java 中,使用对象一定需要通过引用的方式来使用. (有个例外,匿名对象,但是它执行完之后就被回收了). 如果一个对象没有任何引用指向它, 就视为是无法在代码中使用, 就可以回收掉.

5.1.1 引用计数算法.

给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1;
任何时刻计数器为0的对象就是不能再被使用的,即对象已"死".

引用计数法实现简单,判定效率也比较高,在大部分情况下都是一个不错的算法。比如Python语言就采用引用计数法进行内存管理.


在主流的JVM中没有选用引用计数法来管理内存,最主要的原因就是引用计数法无法解决对象的循环引用问题.

为了解释循环引用问题, 写出下列伪代码:

class Test {Test t;
}
main() {Test a = new Test();Test b = new Test();a.t = b;b.t = a;
}

在这里插入图片描述

令 a = null, b = null;

在这里插入图片描述

此时虽然Test对象的引用计数为 1, 但确实无法使用, 也无法被回收.

5.1.2 可达性分析算法


在上面讲了,Java并不采用引用计数法来判断对象是否已"死",而采用可达性分析来判断对象是否存活(同样采用此法的还有C#、Lisp-最早的一门采用动态内存分配的语言).


此算法的核心思想为 : 通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象是不可用的。以下图为例:

在这里插入图片描述

对象Object5-Object7之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此他们会被判定为可回收对象。


JVM 自身知道一共有哪些对象, 通过可达性分析的遍历, 把可达的对象都标记出来, 剩下的自然就是不可达的.

5.2 垃圾回收算法


将死亡对象标记出来了,标记出来之后我们就可以进行垃圾回收操作了.

5.2.1 标记-清除算法


标记-清除"算法是最基础的收集算法。算法分为"标记"和"清除"两个阶段 : 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 后续的收集算法都是基于这种思路并对其不足加以改进而已.

"标记-清除"算法的不足主要有两个 :

1. 效率问题 : 标记和清除这两个过程的效率都不高.

2. 空间问题 : 标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行中需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集。

在这里插入图片描述

5.2.2 复制算法


"复制"算法是为了解决"标记-清理"的效率问题。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉. 这样做的好处是每次都是对整个半区进行内存回收,内存分配时也就不需要考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配即可。此算法实现简单,运行 , 高效。


在这里插入图片描述

5.2.3 标记-整理算法

引入概念: 对象的年龄.
JVM 中有专门的线程负责周期性扫描/释放一个对象(初始年龄相当于是 0), 如果被线程扫描了一次,不是垃圾,年龄就+1.
JVM 中就会根据对象年龄的差异,把整个堆内存分成两个大的新生代(年龄小的对象)和老年代(年龄大的对象).


复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法.

针对老年代的特点,提出了⼀种称之为"标记-整理算法"。标记过程仍与"标记-清除"过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存.

在这里插入图片描述

5.2.4 分代算法


分代算法和上面讲的 3 种算法不同,分代算法是通过区域划分,实现不同区域和不同的垃圾回收策略,从而实现更好的垃圾回收.

这个算法并没有新思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记-清理"算法。

老年代中有大量对象存活,是因为如果要死亡, 在新生代中早就死了, 能活到老年代的对象,说明其生命周期长.

总结来看:

新生代中, 只有少量存活,因此我们采用复制算法; 老年代中有大量存活, 采用"标记-清理"算法.







本篇博客到这里就结束啦, 感谢观看 ❤❤❤

🐎期待与你的下一次相遇😊😊😊

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

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

相关文章

字符串---Spring字符串基本处理

一、String类的特性 不可变性 String对象一旦创建,内容不可更改,任何修改操作都会生成新对象。字符串常量池 字符串字面量(如"abc")直接存储在常量池中,重复字面量共享同一内存地址。创建方式 虽然都是字符…

26考研——中央处理器_CPU 的功能和基本结构(5)

408答疑 文章目录 一、CPU 的功能和基本结构CPU 的功能CPU 的基本结构运算器控制器 CPU 的寄存器运算器中的寄存器控制器中的寄存器 八、参考资料鲍鱼科技课件26王道考研书 九、总结 一、CPU 的功能和基本结构 CPU 的功能 中央处理器(CPU)由运算器和控…

传统数据展示 vs 可视化:谁更打动人心?

数据,每天都在我们身边流动:从你手机里的健康步数,到企业财报中的营收增长,再到国家发布的经济指标。但问题是——你怎么“看”这些数据? 过去,我们习惯用表格、文字和报告来展示数据,这种方式…

Base64 编码原理详细解析

Base64 编码是一种常见的数据编码方式,它将二进制数据转化为可打印的 ASCII 字符串。Base64 编码广泛应用于电子邮件、URL 编码、HTTP 请求和响应中等场景。它的核心作用是让二进制数据可以通过仅支持文本的协议或媒介进行传输。本文将更深入地探讨 Base64 编码的原…

一周学会Pandas2 Python数据处理与分析-Pandas2数据排序操作

锋哥原创的Pandas2 Python数据处理与分析 视频教程: 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili Pandas 2提供了多种灵活的数据排序方法,主要针对 DataFrame 和 Series 对象。 1. 按值排序:s…

计算机二级(C语言)已过

非线性结构:树、图 链表和队列的结构特性不一样,链表可以在任何位置插入、删除,而队列只能在队尾入队、队头出队 对长度为n的线性表排序、在最坏情况下时间复杂度,二分查找为O(log2n),顺序查找为O(n),哈希查…

Windows Server 2025开启GPU分区(GPU-P)部署DoraCloud云桌面

本文描述在ShareStation工作站虚拟化方案的部署过程。 将服务器上部署 Windows Server、DoraCloud,并创建带有vGPU的虚拟桌面。 GPU分区技术介绍 GPU-P(GPU Partitioning) 是微软在 Windows 虚拟化平台(如 Hyper-V)中…

Android RxJava框架分析:它的执行流程是如何的?它的线程是如何切换的?如何自定义RxJava操作符?

目录 RxJava是什么?为什么使用。RxJava是如何使用的呢?RxJava如何和Retrofit一起使用。RxJava源码分析。 (1)他执行流程是如何的。(2)map(3)线程的切换。 如何自定义RxJava操作符…

QT的初始代码解读及其布局和弹簧

this指的是真正的当前正在显示的窗口 main函数: Widget w是生成了一个主窗口,QT Designer是在这个主窗口里塞组件 w.show()用来展示这个主窗口 头文件: namespace Ui{class Widget;}中的class Widget和下面的class Widget不是一个东西 Ui…

什么是AI写作

一、AI写作简介 AI 写作正在成为未来 10 年最炙手可热的超级技能。已经有越来越多的人通过 AI 写作,在自媒体、公文写作、商业策划等领域实现了提效,甚至产生了变现收益。 掌握 AI 写作技能,不仅能提高个人生产力,还可能在未来的 …

13.原生测试框架Unittest解决用例组织问题 与测试套件的使用

13. 原生测试框架Unittest解决用例组织问题 与测试套件的使用 一、测试架构核心组件解析 1.1 系统组成模块 #mermaid-svg-bYie0B3MLRp0HL4g {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bYie0B3MLRp0HL4g .erro…

UE5 脚部贴地不穿过地板方案

UE自带的IK RIG和ControlRig技术 【UE5】角色脚部IK——如何让脚贴在不同斜度的地面(设置脚的旋转)_哔哩哔哩_bilibili 实验后这个还是有一部分问题,首先只能保证高度不能穿过,但是脚步旋转还是会导致穿模 IK前,整个模型在斜坡上会浮空 参考制作:https://www.youtube.com/w…

关于 js:4. 异步机制与事件循环

一、同步 vs 异步 1. 什么是同步(Synchronous) 同步代码就是一行一行、按顺序执行的。当前行没有执行完,下一行不能动。 示例: console.log("A"); console.log("B"); console.log("C");输出&am…

如何通过外网访问内网?对比5个简单的局域网让互联网连接方案

在实际应用中,常常需要从外网访问内网资源,如远程办公访问公司内部服务器、在家访问家庭网络中的设备等。又或者在本地内网搭建的项目应用需要提供互联网服务。以下介绍几种常见的外网访问内网、内网提供公网连接实现方法参考。 一、公网IP路由器端口映…

java的输入输出模板(ACM模式)

文章目录 1、前置准备2、普通输入输出API①、输入API②、输出API 3、快速输入输出API①、BufferedReader②、BufferedWriter 案例题目描述代码 面试有时候要acm模式,刷惯leetcode可能会手生不会acm模式,该文直接通过几个题来熟悉java的输入输出模板&…

什么是移动设备管理(MDM)

移动设备管理(MDM)是一种安全解决方案,旨在监控、管理和保护企业的移动设备(包括智能手机、平板电脑、笔记本电脑和计算机)。MDM软件是IT部门的关键工具,其核心功能包括设备配置、安全策略实施、远程控制及…

c++中构造对象实例的两种方式及其返回值

c中,构造对象实例有两种方式,一种返回对象实例,一种返回该对象实例的指针。如下所示: 一、两种返回值 RedisConn conn1; //得到实例conn1;RedisConn *conn2 new RedisConn();//得到指针conn2;RedisConn conn3 new RedisConn()…

【Unity笔记】PathCreator使用教程:用PathCreator实现自定义轨迹动画与路径控制

在Unity开发过程中,角色移动、摄像机动画、轨道系统、AI巡逻等功能中,路径控制是常见又复杂的需求之一。如何优雅、高效地创建路径并控制对象沿路径运动,是游戏开发、动画制作乃至工业仿真中的关键问题。 在这篇文章中,我将介绍一…

JAVA实战开源项目:健身房管理系统 (Vue+SpringBoot) 附源码

本文项目编号 T 180 ,文末自助获取源码 \color{red}{T180,文末自助获取源码} T180,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

[人机交互]交互设计过程

*一.设计 1.1什么是设计 设计是一项创新活动,旨在为用户提供可用的产品 –交互设计是“设计交互式产品、以支持人们的生活和工作” 1.2设计包含的四个活动 – 识别用户的需要( needs )并建立需求( requirements &…