JVM理解学习

news2024/11/20 2:36:15

参考视频

运行时数据区

JVM架构总览图

绿色的:方法区,堆,是所有线程共享的

黄色的: 虚拟机栈,本地方法栈,程序计数器,是线程私有的

程序计数器

        程序计数器是一块较小的内存空间,物理上用寄存器实现,可以看作当前线程所执行的字节码的行号指示器。

        作用: 记住下一条JVM指令的执行地址

        特点

                1 是线程私有的,随着线程的创建而创建,随着线程的消息而消息

                2 是一小块内存

                3 唯一一个不会内存溢出的内存区域

介绍

        :程序运行需要的内存空间

        虚拟机栈: Java方法执行的线程内存模型,即每个线程运行时所需要的内存

        数据结构:先进(压栈)后出(出栈)

       每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存储局部变量表,操作数栈,动态连接,方法出口等信息。每个方法被调用执行完毕的过程,就对应一个栈帧在虚拟机中入栈到出栈的过程。

        一个栈可以看成多个栈帧组成,每个栈帧可以看成每个方法的运行时需要的内存(参数,局部变量,返回地址等)

        定义

                1 每个线程运行时所需要的内存,成为虚拟机栈

                2 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

                3 每个线程只能有一个活动栈帧,活动栈帧即当前正在执行的那个方法

问题辨析:

1 垃圾回收是否涉及栈内存?

答:不需要。 每次方法结束后都会出栈,自动被回收,所以不需要垃圾回收。

2 栈内存分配越大越好吗?

答:不是。内存是有限的,栈内存越大,线程越少。

Linux/MacOs/Oracle Solaris : 栈内存大小默认1024k

-Xss1024k

3 方法内的局部变量是否线程安全?

答:如果方法内 局部变量没有逃离方法的作用范围,它是线程安全的。如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

栈内存溢出
  1. 栈帧过多,即调用的方法过多,最容易产生的:递归调用(测试2w多次递归会报错)
  2. 栈帧过大,不太容易出现

本地方法栈(native method stacks)

        不是由java代码编写的方法,java用本地方法调用底层的c或c++使用的方法

        与虚拟机栈发挥的作用非常相似,区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈是为虚拟机使用到的本地(Native)方法服务。

        给本地的方法的运行提供内存空间

        线程私有

堆(Heap)

介绍

        通过new关键字,创建对象都会使用堆内存

特点:
  1. 它是线程共享的,在虚拟机启动时创建
  2. 唯一目的:存放对象实例
  3. Java堆是垃圾收集器管理的内存区域,也被成为“GC堆”
  4. 虚拟机所管理的最大内存
  5. 可以处于物理上不连续的两块空间内,但在逻辑上应该被视为连续的。
堆内存溢出

配置堆内存大小:-Xmx 最大Java堆空间大小,-Xms 初始Java堆

方法区

定义

        方法区是一个逻辑上的概念,也被称为非堆(Non-Heap),一般用来存储类加载信息static静态变量即时编译器编译后的代码缓存数据常量池(Constants Pool)等。

        不同版本的Java其方法区的实现方式不同,在JDK 8之前,采用的是“永久代”来实现方法区,而在JDK 8之后则是采用MetaSpace(元空间)的方式来实现,元空间移到本地内存(native memory)里,存储类的元数据信息,而字符串常量池(也成为串池)和静态变量移到堆中。

        元空间的大小受限于本地内存的大小,可以动态地扩展,减轻了类元数据区溢出的问题

        垃圾收集在这里非常少,这区域的回收主要是常量池的回收和对类型的卸载

  • 共享性: 方法区与Java堆一样,是各个线程共享的内存区域。
  • 创建和内存空间: 方法区在JVM启动时被创建,实际的物理内存空间和Java堆一样,可以是不连续的。
  • 大小和可扩展性: 方法区的大小,就像堆空间一样,可以选择固定大小或可扩展。
  • 溢出问题: 方法区的大小决定了系统能够保存多少个类。如果系统定义了太多的类,导致方法区溢出,虚拟机将抛出内存溢出错误,例如 java.lang.OutOfMemoryError: PermGen space 或 java.lang.OutOfMemoryError: Metaspace
  • 释放: 关闭JVM将释放方法区的内存空间。

常量池 和 运行时常量池

JVM的常量池主要有以下几种:

  • class文件常量池表
  • 运行时常量池
  • 字符串常量池(StringTable, 也成为串池)
  • 基本类型包装类常量池
Class文件常量池

        每个class的字节码文件中都有一个常量池表,里面是编译后即知的该class会用到的字面量符号引用,这就是class文件常量池。这部分内容会在类加载后放到方法区的运行时常量池。

运行时常量池

        class类信息及其class文件常量池是字节码的二进制流,它代表的是一个类的静态存储结构,JVM加载类时,需要将其转换为方法区中的java.lang.Class类的对象实例;同时,会将class文件常量池中的内容导入运行时常量池

  是方法区的一部分,自然受到方法区内存的限制

 具备动态性,Java语言并不要求常量只有编译期才能产生,运行期间也可以产生新的常量。eg:String类的intern()方法

字符串常量池

        运行时常量池中的常量对应的内容只是字面量,比如一个"字符串",它还不是String对象;当Java程序在运行时执行到这个"字符串"字面量时,会去字符串常量池里找该字面量的对象引用是否存在,存在则直接返回该引用,不存在则在Java堆里创建该字面量对应的String对象,并将其引用置于字符串常量池中,然后返回该引用

基本类型包装类常量池

        Java的基本数据类型中,除了两个浮点数类型,其他的基本数据类型都在各自内部实现了常量池,但都在[-128~127]这个范围内

 参考文档:

【JVM系列】运行时Java类的内存营地——方法区详解 - 知乎

Java基础-JVM内存管理-常量池与运行时常量池 - 简书

串池练习(JDK8)
第一题:
String s1 = "a";
String s2 = "b";
String s3 = "a"+"b";
String s4 = s1 + s2;
String s5 = "ab";
String s6 = s4.intern();

System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
// s3 == s4 false
// s3 == s5 true
// s3 == s6 true
/*
推导:
String s1 = "a" -> "a" 在串池中创建 StringTable["a"]
String s2 = "b" -> 在串池中创建 StringTable["a","b"]
String s3 = "a"+"b"  -> "a","b"都在串池中存在,这里的+号在编译器优化时直接变成"ab",放到常量池中。
        s3 = "ab",在串池中创建 StringTable["a","b","ab"];
String s4 = s1 + s2; -> 字符串拼接,调用StringBuilder,所以最终生成一个堆里的对象, s4 = new String("ab");
String s5 = "ab"; -> 串池中已有"ab",引用串池中的 "ab";
String s6 = s4.intern(); -> intern 是一个 native 的方法,如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回
        所以 s6 = 串池中的"ab"
s3 == s4 : (s3 = "ab") == (s4 = new String("ab")) false,一个串池,一个对象地址引用
s3 == s5 : (s3 = "ab") == (s5 = "ab") true ,两个都指向串池中的字符串
s3 == s6 : (s3 = "ab") == (s6 = "ab") true ,两个都指向串池中的字符串
 */

第二题:
String x1 = "cd";
String x2 = new String("c") + new String("d");
System.out.println(x1 == x2);
// false
/*
推导:
String x1 = "cd" -> "cd" 在串池中创建 StringTable["cd"]
String x1 = "cd" -> "cd" 在串池中创建 StringTable["cd"]
 */

直接内存

        直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域,但这部分内存也被频繁使用,而且也可能导致内存溢出。

        本机直接内存的大小不会受到Java堆大小的限制,但是既然是内存,肯定受到本机总内存大小以及处理器寻址空间的限制。

虚拟机对象

        对象的创建

                创建对象(例外:复制,反序列化)是用new关键字,虚拟机中如何创建的对象(本次讨论不包括数组和Class对象)?

1 Java虚拟机遇到一条字节码new指令 

2 检查指令的参数是否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否加载,解析和初始化过。

        2.1  没有,则执行相应的加载过程

3 虚拟机为新生对象分配内存(内存大小在类加载完成后便确定)

4 内存分配后,虚拟机将分配到的内存空间(但不包括对象头)都初始化为零值

5 Java虚拟机对对象进行设置

        例如:对象是哪个类的实例,如何能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等

6 上面完成后,从虚拟机角度看一个新的对象已经产生。但Java来看,对象创建才刚开始——需要初始化构造函数,即Class文件的<init>方法,字段还没有赋值等。

备注

1 简化流程

        new指令 -> 检查参数是否定位到一个类的符号引用,并检查加载情况 -> 分配内存 -> 初始化为0 -> 对对象设置信息 -> Java的构造函数

2 分配方式:指针碰撞

        步骤3,为新生对象分配内存。假设Java堆中的内存是绝对规整的,所有被使用的内存都放在一起,空闲的放在另一边,中间放一个指针作为分界点的指示器,那分配内存就仅仅是把指针向空虚方向挪动一段与对象大小相等的距离

3 分配方式:空闲列表(Free List)

        步骤3,为新生对象分配内存。假设Java堆里的内存是不规整的,虚拟机则必须维护一个列表,记录哪些内存块可用,在分配的时候从列表中找到一个足够大的空间划分给对象实例,并更新列表上的记录

以上选用哪种分配方式,由Java堆是否规整决定,而Java堆是否规整则由所采用的垃圾收集器是否带有空间压缩整理(Compact)的能力决定。

Serial,ParNew 等带压缩整理过程的收集器时,采用指针碰撞,简单高效

CMS这种基于清除(Sweep)算法的收集器时,采用较为复杂的空闲列表

创建是一个频繁操作,并发时线程并不安全。解决这种情况有两种方案:

方案一

        对分配内存空间的动作进行同步处理——实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;

方案二

        内存分配的动作按照线程划分在不同的空间中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)。

        哪个线程要分配内存,就在哪个线程的本地缓冲区中分配;只有本地缓冲区的内存用完,需要新的缓冲区时才需要同步锁定。

虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设置

        对象的内存布局

        对象在堆内存中的存储布局可以划分为三个部分: 对象头(Header), 实例数据(Instance Data) 和对齐填充(Padding)

        对象头:

      一类数据:存储对象自身的运行时数据:如哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等,这类数据称为“Mark Word”,是动态定义的数据结构。

       另一类数据:类型指针,即对象指向它的类型元数据的指针,确定哪个类的实例。

        实例数据:

       对象真正存储的有效信息。

       这部分存储顺序受到虚拟机分配策略参数(-XX: FieldsAllocationStyle参数)和字段在Java源码中定义顺序的影响。

        HotSpot虚拟机默认的分配顺序:long/doubles ,ints, shorts/chars, bytes/booleans,oops 

        对齐填充:

        对齐填充,并不不是必然存在,也没有特别含义,仅起到占位符的作用。

        Hotspot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,因此对象实例数据部分没用对齐的话,需要对齐填充来补全

        对象的访问定位

        Java程序会通过栈上的reference数据来操作对上的具体对象

        对象访问方式由虚拟机实现而定,主流的有两种:句柄 和 直接指针。

句柄访问:

        Java划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自具体的地址信息。

句柄图如下

直接指针访问:

        reference中存储的直接就是对象地址,如果访问对象本身的话,不需要多一次间接访问开销。

直接指针访问图:

        两种对象访问各有优势。

        句柄访问最大好处是reference存储的是稳定的句柄地址,在对象被移动(GC行为)时只改变句柄中的实例数据指针,而reference本身不需要被修改

        直接指针访问最大好处就是速度快,节省了一次指针定位的时间开销。Hotspot采用的就是这种方式访问

垃圾收集器与内存分配策略

针对垃圾回收对堆内存回收前,判断对象是否存活有两种算法:1 引用计数算法,2 可达性分析算法

引用计数算法:

        在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1,当引用失效,计数器值减1;任何时刻计算器为0的对象就是不可能再被使用的。

        原理简单,判定效率高,是个不错的算法。

        但JVM没有使用它,原因:这种算法有很多例外情况需要考虑,必须配合大量额外处理才能保证正确的工作,比如相互引用。

objA.child = objB
objB.child = objA

可达性分析算法

        当前主流的商用程序语言(Java,C#)的内存管理子系统,都是通过可达性分析算法判断对象是否存活。

基本思路:

        通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”。

        如果某个对象到“GC Roots”之间没有任何引用链相连,则证明此对象是不可能再被使用的。

在Java体系里,固定作为GC Roots的对象包括以下几种:

  1. 在虚拟机栈(栈帧中的本地变量表)中引用的对象,eg: 当前正在运行的方法所使用到的参数、局部变量、临时变量等。
  2. 在方法区中类静态属性引用的对象,eg:Java类的引用类型静态变量
  3. 在方法区中常量引用的对象,eg:串池(String Table)里的引用
  4. 在本地方法栈中JNI(通常指Native方法)引用的对象
  5. Java虚拟机内部的引用,eg:基本数据类型对应的Class对象,一些常驻的异常情况等,还有系统类加载器
  6. 所有被同步锁(synchronized)持有的对象
  7. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调,本地代码缓存等

四大引用

Java对引用的概念分为四大类:强引用,软引用,弱引用,虚引用

强引用:

        最传统的“引用”定义,即 "Object obj = new Object()" 

        无论任何情况下,只要强引用关系存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用:

        描述一些还有用,但非必须得对象。

        软引用关联的对象,在系统将要发生内存溢出异常前,会把这些对象 列进 回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会跑出内存溢出异常。

        JDK1.2版之后提供SoftRelerence类来实现软引用。

弱引用:

        描述那些非必须对象,但是它的强度比软引用更弱,被弱引用的对象只能生存到下一次垃圾收集发生为止。

        当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉。

        JDK1.2版之后提供WeakReference类来实现弱引用。

虚引用:

        也被称为“幽灵引用”,或者“幻影引用”,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。

        虚引用的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

        JDK1.2版之后提供了PhantomReference类来实现虚引用

对象收集过程

可达性分析算法判定不可达的对象,也不是“非死不可”。

过程:

        可达性分析算法判断不可达的对象 -》第一次标记 -》此对象是否有必要执行 finalize()方法 -》

        1 假如对象没有覆盖finalize()方法,或finalize()已经被虚拟机调用过 -》则没必要回收

        2 假如对象判定有必要执行finaliz() -》放置到一个F-Queue的队列中 -》由虚拟机自动建立、低调度优先级的Finalizer线程去执行它们的finalize()方法

备注:

        执行Finalize方法指虚拟机会触发这个方法开始运行,但不承诺一定会等待它运行结束。原因:如果某个对象的finalize()方法执行缓慢,或者发生了死循环,很可能导致F-Queue队列中的其他对象出于等待,导致整个内存回收子系统的崩溃。

        finalize()方法是对象逃脱死亡命运的最后一次计划,稍后收集器将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可。

        任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行。

方法区的垃圾收集

        方法区的垃圾收集的性价比很低,主要收集两部分内容: 废弃的常量 不再使用的类型

垃圾收集算法

        垃圾收集算法 可以划分 :引用计数式垃圾收集 (Reference CountIng GC, 不讨论) 和 追踪式垃圾收集 (Tracing GC

分代收集理论

分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分带假说之上:

  1. 弱分代假说: 绝大多数对象都是朝生夕灭的。
  2. 强分代假说: 熬过越多次垃圾收集过程的对象就越难以消亡。

基于此确定了垃圾收集器的设计原则:

        收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(即熬过垃圾收集过程的次数)分配到不同的区域之中存储。

        朝生夕死的对方放在同一个区域,就能以较低代价收集到大量空间。

所以基于分代收集两个假说确定了Java堆划分不同区域来垃圾收集的基调。

Minor GC,Magor GC, Full GC        

参考文档:「JVM」Full GC和Minor GC、Major GC_jvm什么时候minor gc-CSDN博客

垃圾回收操作分为:Partial GC(Minor/Young GC,Magor/Old GC) , Full GC 

Partial GC :部分收集

        - Minor GC:新生代收集

        - Magor GC :老年代收集

        - Mixed GC:混合收集,目前只在G1收集器有。

Full GC:整堆收集

java堆分为:新生代,老年代

新生代中每次垃圾收集(Minor GC)都有大量对象死去,少量的存活对象逐步晋升老年代。

垃圾收集算法

标记-清除算法,标记-复制算法,标记-整理算法

参考文档:JAVA开发(JAVA垃圾回收的几种常见算法)_java垃圾回收算法-CSDN博客

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

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

相关文章

蓝桥杯单片机快速开发笔记——矩阵键盘

一、原理分析 二、示例框架 定义了四个位控制变量&#xff0c;用于控制键盘扫描时的行列信号。 在Scan_Keys()函数中&#xff0c;首先设置行列信号&#xff0c;将其中一个行信号置为0&#xff0c;另一个行信号置为1&#xff0c;同时将列信号置为1&#xff0c;用于扫描键盘按键…

力扣映射题:机器人能否返回原点

超时太多了&#xff0c;笔者也开始放弃遍历了 bool judgeCircle(char* moves) {int arr[4]{0};for(int x0;x<strlen(moves);x) {if(moves[x]R){arr[0]arr[0]1;}if(moves[x]L){arr[1]arr[1]1;}if(moves[x]U){arr[2]arr[2]1;}if(moves[x]D){arr[3]arr[3]1;}} if(arr[0]-arr[1…

php彩虹/异世界云商全解系统

系统更新与修复列表 1. 基于彩虹的二次开发 - 对彩虹系统进行了二次开发&#xff0c;增强了系统的功能和性能。2. 新增自定义输入框提示内容&#xff08;支持批量修改&#xff09; - 用户可以自定义输入框的提示内容&#xff0c;并支持批量修改&#xff0c;提升用户体验。3. 新…

掌握高级设计原则:Java中的过滤器模式解析与实战演练,构建灵活且可扩展的系统架构

过滤器模式是一种结构型设计模式&#xff0c;它允许开发者使用不同的标准来过滤一组对象&#xff0c;并通过逻辑运算以解耦的方式将它们联系起来。 过滤器模式的核心在于提供了一个处理对象的机制&#xff0c;这个机制可以根据一个或多个标准来决定哪些对象应该被接受、哪些应…

代码学习记录21--回溯算法第二天

随想录日记part21 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.16 主要内容&#xff1a;今天主要是结合类型的题目加深对回溯算法的理解&#xff1a;1&#xff1a;组合总和&#xff1b;2&#xff1a;电话号码的字母组合 216.组合总和III17.电话号码的字母…

快速了解微软推出的开发人员主页的应用

一、概述 开发人员主页是微软推出的一个新的 Windows 控制中心&#xff0c;提供以下功能&#xff1a;使用可自定义小组件监视仪表板中的项目&#xff0c;通过下载应用、包或存储库来设置开发环境&#xff0c;连接到开发人员帐户和工具&#xff08;如 GitHub&#xff09;&#x…

【C++进阶】深度解析AVL树及其简单模拟实现

AVL树的解析和模拟实现 一&#xff0c;什么是AVL树二&#xff0c;AVL树的特性三&#xff0c;模拟实现1. 基本框架2. 插入&#xff08;不带旋转&#xff09;2. AVL树的旋转3. AVL树的验证 四&#xff0c;总结 一&#xff0c;什么是AVL树 之前我们学习了二叉搜索树&#xff0c;但…

【每日力扣】40.组合总和II与701. 二叉搜索树中的插入操作

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害。 40.组合总和II 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为…

【小白学机器学习8】统计里的自由度DF=degree of freedom, 以及关于df=n-k, df=n-k-1, df=n-1 等自由度公式

目录 1 自由度 /degree of freedom / df 1.1 物理学的自由度 1.2 数学里的自由度 1.2.1 数学里的自由度 1.2.2 用线性代数来理解自由度&#xff08;需要补充&#xff09; 1.2.3 统计里的自由度 1.3 统计学里自由度的定义 2 不同对象的自由度 2.1 纯公式的自由度&#…

报Invalid value type for attribute ‘factoryBeanObjectType‘: java.lang.String错误

在springboot中使用Mybatis出现Invalid value type for attribute factoryBeanObjectType: java.lang.String 1、没有使用mybatis 检查pom文件里面的mybatis 可能是缺少这个依赖&#xff0c;或者版本过低 重新导入依赖 <dependency><groupId>org.mybatis.spri…

华为数通方向HCIP-DataCom H12-821题库(多选题:141-160)

第141题 以下关于802.1X认证的触发机制,描述正确的有? A、802.1X认证不能由认证设备(如802.1交换机)发起 B、802.1X客户端可以组播或广播方式触发认证 C、认证设备可以以组播或单播方式触发认证 D、802.1X认证只能由客户端主动发起 【参考答案】BC 【答案解析】 第142题 以…

集合系列(二) -List接口详解

一、List简介 List 的数据结构就是一个序列&#xff0c;存储内容时直接在内存中开辟一块连续的空间&#xff0c;然后将空间地址与索引对应。 以下是List集合简易架构图 由图中的继承关系&#xff0c;可以知道&#xff0c;ArrayList、LinkedList、Vector、Stack都是List的四个…

B3620 x 进制转 10 进制(详解)

题目 思路 八进制数567怎么转化为十进制数。首先八进制就是逢八进一&#xff0c;也就是说这里面最大的数也就7&#xff0c;没有≥8的数。下面我们就讲一下567怎么转化为十进制&#xff1a;首先7是个位&#xff0c;可以直接写成十进制的7&#xff0c;6是十位&#xff0c;它是通…

springboot基于java的畅销图书推荐系统

摘 要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一…

AI_寻路系统_修改寻路网格体

学习笔记&#xff0c;仅供参考&#xff01; 一、完成创建关卡和AI代理的初步步骤&#xff0c;以演示可以修改导航系统的不同方法。 创建简单关卡&#xff0c;并通过在关卡中放入导航网格体边界体积Actor来添加导航。 将ThirdPersonCharacter蓝图修改为使用导航系统在关卡中四…

vuepress-theme-vdoing博客搭建教程

搭建流程 前言 这是笔者搭建个人博客所经历的流程&#xff0c;特附上笔记 笔者个人博客地址&#xff1a;沉梦听雨的编程指南 一、主题介绍 本博客使用的主题为&#xff1a;vuepress-theme-vdoing&#xff0c;相关介绍和使用方法可以参考该主题的官方文档 官方文档快速上手…

力扣趣味题:找不同

经典面向样例编程 char findTheDifference(char* s, char* t) {if(sNULL){return t[0];}for(int x0;x<strlen(s);x){for(int y0;y<strlen(t);y){if(s[x]t[y]){t[y]1;break;}}}for(int x0;x<strlen(t);x){if(t[x]!1){return t[x];}}return NULL; }

银发经济@315:消费、陷阱与孤独的老人

【潮汐商业评论/文】 又是一年315。 这一天&#xff0c;从品牌到消费者&#xff0c;从线下到网络&#xff0c;都不约而同地将目光锁定在大众消费生活和与其相伴的消费“陷阱”上。 这其中&#xff0c;作为“有闲又有钱”且与社会经济发展速度相对有一定“代沟”的老年消费者群…

新加坡大带宽服务器托管优势

在数字化快速发展的今天&#xff0c;服务器托管成为企业拓展业务、提高服务质量的关键环节。而新加坡作为一个国际性的金融、贸易和科技创新中心&#xff0c;其大带宽服务器托管服务在全球范围内享有盛誉。本文将为您科普新加坡大带宽服务器托管的诸多优势。 首先&#xff0c;新…

AXI CANFD MicroBlaze 测试笔记

文章目录 前言测试用的硬件连接Vivado 配置Vitis MicroBlaze CANFD 代码测试代码测试截图Github Link 前言 官网: CAN with Flexible Data Rate (CAN FD) (xilinx.com) 特征: 支持8Mb/s的CANFD多达 3 个数据位发送器延迟补偿(TDC, transmitter delay compensation)32-deep T…