对象创建的主要流程

在这里插入图片描述

  1. 类加载检查
    在创建对象之前,JVM 首先会检查该类是否已经加载、解析并初始化:
    如果没有,则会通过类加载机制加载类元信息(Class Metadata)到方法区。
    这个过程包括:加载(load)、验证(verify)、准备(prepare)、解析(resolve)、初始化(init)。

new关键词、对象克隆、对象序列化等

  1. 分配内存
    对象的内存分配通常发生在堆内存中(Heap):
    JVM 使用了两种主要的内存分配方式
分配方式说明
指针碰撞(Bump-the-pointer)(默认用指针碰撞)适用于堆空间规整;分配快,只需移动指针。
空闲列表(Free List)适用于堆空间不规整(存在碎片);需要维护空闲块链表,分配效率低一些。(通常在垃圾回收后出现堆空间不规整)

以上两种方法都存在并发问题,解决并发问题的方法:

  • CAS(compare and swap)
    虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。
  • 本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
    把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存。通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。
  1. 内存初始化(零值初始化)
    分配的内存空间会被零值初始化(不包括对象头),也就是将对象的实例字段全部设为默认值(0、null、false 等)。如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。

注意:这只是 JVM 级别的初始化,字段的显式初始值(如 int a = 10)还未处理。

  1. 设置对象头
    在这里插入图片描述

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、 实例数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机的对象头包含两个主要部分:

  • Mark Word:记录哈希码、GC 分代信息、锁信息等。
  • Klass Pointer:指向对象的类元信息(方法区存的类元数据)(即该对象是哪个类的实例)。

64 位 JVM 默认启用了指针压缩(CompressedOops),否则指针是 64 bit,Klass Pointer 就会是 8 byte。

  • 32位对象头
    在这里插入图片描述
  • 64位对象头
    在这里插入图片描述

数组对象在 JVM 中的对象头结构 相比普通对象稍有不同,因为它除了包含普通对象头,还需要4字节存储数组长度。

分代年龄4字节也验证了只能小于等于15

  1. 执行< init >方法
    执行< init >方法,即对象按照程序员的意愿进行初始化。对应到语言层面上讲,就是为属性赋值(注意,这与上面的赋零值不同,这是由程序员赋的值),和执行构造方法。

对象大小与指针压缩

<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version>
</dependency>
import org.openjdk.jol.info.ClassLayout;/*** 计算对象大小*/
public class JOLSample {public static void main(String[] args) {ClassLayout layout = ClassLayout.parseInstance(new Object());System.out.println(layout.toPrintable());System.out.println();ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});System.out.println(layout1.toPrintable());System.out.println();ClassLayout layout2 = ClassLayout.parseInstance(new A());System.out.println(layout2.toPrintable());}// -XX:+UseCompressedOops           默认开启的压缩所有指针// -XX:+UseCompressedClassPointers  默认开启的压缩对象头里的类型指针Klass Pointer// Oops : Ordinary Object Pointerspublic static class A {//8B mark word//4B Klass Pointer   如果关闭压缩-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,则占用8Bint id;        //4BString name;   //4B  如果关闭压缩-XX:-UseCompressedOops,则占用8Bbyte b;        //1B Object o;      //4B  如果关闭压缩-XX:-UseCompressedOops,则占用8B}
}运行结果:
java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)    //mark word4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)    //mark word     8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)    //Klass Pointer12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total[I object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)16     0    int [I.<elements>                             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalcom.tuling.jvm.JOLSample$A object internals:OFFSET  SIZE               TYPE DESCRIPTION                               VALUE0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4                    (object header)                           61 cc 00 f8 (01100001 11001100 00000000 11111000) (-134165407)12     4                int A.id                                      016     1               byte A.b                                       017     3                    (alignment/padding gap)                  20     4   java.lang.String A.name                                    null24     4   java.lang.Object A.o                                       null28     4                    (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
  • 什么是java对象的指针压缩?
  1. jdk1.6 update14开始,在64bit操作系统中,JVM支持指针压缩
  2. jvm配置参数:
    -XX:+UseCompressedOops 默认开启的压缩所有指针(关闭同时会关闭UseCompressedClassPointers ),
    -XX:+UseCompressedClassPointers 默认开启的压缩对象头里的类型指针Klass Pointer
    (compressed–压缩、oop(ordinary object pointer)–对象指针)
  3. 启用指针压缩:-XX:+UseCompressedOops(默认开启),禁止指针压缩:-XX:-UseCompressedOops

指针压缩(Compressed Oops)主要是为了解决 64 位 JVM 指针过大带来的内存和性能问题。
64 位指针占用 8 字节,是 32 位指针的两倍(4 字节)。Java 堆中对象众多,对象之间通过指针相互引用,指针占内存总量很大。指针变大导致:堆内存占用增加,同样大小的堆实际能存放的对象数量减少。CPU 缓存压力变大,缓存命中率下降,性能下降。内存带宽需求增加,访问延迟提高。指针压缩将 64 位指针压缩成 32 位偏移量,减少内存占用。

  1. 堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
  2. 堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址,这就会出现堆内存占用增加等问题,所以堆内存不要大于32G为好(最大堆大小 ≈ 压缩指针可表示的偏移范围 × 缩放因子(scale)最大堆地址范围 = 2^32 × 8 = 34,359,738,368 字节 ≈ 32GB)

关于对齐填充:对于大部分处理器,对象以8字节整数倍来对齐填充都是最高效的存取方式。

对象内存分配

对象内存分配流程

在这里插入图片描述

对象栈上分配(逃逸分析)

JVM通过逃逸分析判断对象是否只在方法内部使用,若没有逃逸,可以将对象分配在栈上(栈上分配)或通过标量替换消除对象,减少堆分配压力。

  • 对象逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中。 JVM对于这种情况可以通过开启逃逸分析参数(-XX:+DoEscapeAnalysis)来优化对象内存分配位置,使其通过标量替换优先分配在栈上(栈上分配),JDK7之后默认开启逃逸分析,如果要关闭使用参数(-XX:-DoEscapeAnalysis)
  • 标量替换(标量替换聚合量):通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。开启标量替换参数(-XX:+EliminateAllocations),JDK7之后默认开启。
  • 标量与聚合量:标量即不可被进一步分解的量,而JAVA的基本数据类型就是标量(如:int,long等基本数据类型以及reference类型等),标量的对立就是可以被进一步分解的量,而这种量称之为聚合量。而在JAVA中对象就是可以被进一步分解的聚合量。

对象在Eden区分配

新生代分为 Eden区 和两个 Survivor区(S0、S1)Eden与Survivor区默认8:1:1,新生代对象绝大多数先在 Eden 区分配。大多数对象生命周期短暂,及时GC回收效率高,Eden区空间一般较大,分配速度快。
当 Eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
Minor GC和Full GC

  • Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
    触发minor gc就会会把eden和from清空,并且把两个区域还存活的对象移动到to
  • Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上。
    就是执行一次Minor GC+老年代的回收+方法区的回收

JVM默认有这个参数-XX:+UseAdaptiveSizePolicy(默认开启),会导致这个8:1:1比例自动变化,如果不想这个比例有变化可以设置参数-XX:-UseAdaptiveSizePolicy

-XX:+PrintGCDetails 是 JVM 的一个参数,用于打印垃圾回收(GC)时的详细日志信息。它是调试和性能分析GC行为的常用工具。

大对象直接进入老年代

当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC,GC期间虚拟机又发现无法存入Survior空间,所以只好把新生代的对象提前转移到老年代中去,老年代上的空间足够存放,所以不会出现Full GC。

大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 -XX:PretenureSizeThreshold(单位字节) 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下有效。这样可以避免为大对象分配内存时的复制操作而降低效率。

长期存活的对象将进入老年代

对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1。对象在 Survivor 中每熬过一次 MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同)就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 **-XX:MaxTenuringThreshold(最大值硬编码为 15,解释对象头有提到) **来设置。

对象动态年龄判断

JVM 会根据 Survivor 区当前对象年龄的分布动态决定对象是否需要提前进入老年代,即使它们还没有达到 MaxTenuringThreshold。
当前放对象的Survivor区域里(to space),一批对象的总大小大于这块Survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象直接进入老年代

例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。

动态年龄判断触发对象直接进入老年代,发生在每次 Minor GC 执行时的对象复制过程中。

当 Minor GC 把 Eden 和 From 区的存活对象复制到 To Space 时,如果即将进入 To Space 的对象总大小超过 To Space 容量的一半(即 Survivor 区目标大小的一半),会导致这些对象直接进入老年代。

老年代空间分配担保机制

老年代空间分配担保机制(也称为“晋升空间担保”)是 JVM 中一项重要的安全机制,可以实现提前进行full gc。并且确保在 minor GC 时,幸存对象能够安全晋升到老年代,不会因为老年代空间不足导致堆内存溢出或对象丢失。
担保机制的流程:

  1. minor GC 准备阶段:JVM 会计算老年代剩余可用空间是否小于年轻代里现有的所有对象大小之和(包括垃圾对象)
  2. 如果空间足够,minor GC 继续执行,晋升操作顺利完成。
  3. 如果空间不足,就会看一个-XX:-HandlePromotionFailure(jdk1.8默认就设置了)的参数是否设置了,如果有这个参数,就会看看老年代的可用内存大小,是否大于之前每一次minor gc后进入老年代的对象的平均大小。老年代的可用内存大小依然不足之前进入老年代的平均大小就full gc,足够则minor gc
  4. 如果老年代剩余空间不足并且参数没有设置,就会触发一次full gc,对老年代和年轻代一起回收一次垃圾,如果回收完还是没有足够空间存放新的对象就会发生"OOM"

当然,如果minor gc之后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间,那么也会触发full gc,full gc完之后如果还是没有空间放minor gc之后的存活对象,则也会发生“OOM”

对象内存回收

引用计数法(jvm一般不用)

每个对象维护一个引用计数器(refCount),每当有一个地方引用它,计数器加 1;引用失效时,计数器减 1。当引用计数为 0,就说明该对象不再被使用,可以被回收。致命缺点:循环引用问题。引用计数法无法处理两个对象互相引用但整体不可达的情况。

public class ReferenceCountingGc {Object instance = null;public static void main(String[] args) {ReferenceCountingGc objA = new ReferenceCountingGc();ReferenceCountingGc objB = new ReferenceCountingGc();objA.instance = objB;objB.instance = objA;objA = null;objB = null;}
}

除了对象objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数算法无法通知 GC 回收器回收他们。

可达性分析算法(jvm一般使用)

在这里插入图片描述
将“GC Roots” 对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象
常见的 GC Roots 包括:

GC Roots 类型示例
虚拟机栈中的引用变量(栈帧中的局部变量)方法中定义的局部变量、参数等
方法区中静态字段引用的对象static 引用的对象
方法区中常量引用的对象字面量池中的字符串,如 "abc"
本地方法栈中的 JNI 引用Native 方法中用到的对象
运行时的活动线程启动的线程本身不会被 GC 回收
类加载器系统/应用类加载器

因为stw了,还在活跃但是被暂停的栈帧的对象不该被回收

三色标记(并发垃圾回收器(如 CMS、G1))

三色标记算法(Tri-color Marking)是现代垃圾回收(GC)中常用的可达性分析方法,特别用于并发和增量标记阶段,它有效避免了标记过程中出现对象“丢失”的问题。

三色含义
白色(White)表示未被访问的对象集合。初始时,所有对象都是白色。标记完成后,仍为白色的对象被判定为不可达对象,需要回收。
灰色(Gray)示已经被发现,但其引用的对象尚未完全扫描的对象集合。需要扫描灰色对象的引用,进一步递归标记。
黑色(Black)表示该对象以及它引用的所有对象都已经被扫描完成。

算法流程:

  1. 初始化:所有对象为白色。从 GC Roots 开始,将这些根对象标记为灰色。
  2. 扫描灰色对象:从灰色对象集合中取出一个对象,扫描它引用的所有白色对象。发现的白色对象变成灰色,加入待扫描集合。扫描完成后,当前对象变黑色。
  3. 重复扫描灰色对象,直到灰色集合为空。此时所有可达对象都变成了黑色。剩下的白色对象即不可达,准备回收。

三色标记通过保持“黑色对象不引用白色对象”的不变式,保证标记的正确性。
当应用线程修改引用时,如果黑色对象引用变成了白色对象,GC必须检测到这个“新引用的白色对象”。
GC使用写屏障(Write Barrier)机制,捕获这类修改,并将被新引用的白色对象标记为灰色,加入扫描队列,保证不会遗漏。

常见引用类型

Java 四种引用类型(按强弱排序):

引用类型类名是否会阻止 GC回收时机描述
强引用普通变量引用(默认)会阻止回收永远不会被 GC 回收,除非手动断开引用
软引用java.lang.ref.SoftReference一定条件下回收内存不足时才会被回收,适合缓存
弱引用java.lang.ref.WeakReference不阻止回收下一次 GC 就会被回收
虚引用java.lang.ref.PhantomReference完全不保留被 GC 回收前就进入 ReferenceQueue,仅作通知
  • 强引用:普通的变量引用
public static User user = new User();//静态变量
public Object foo() {Object obj = new Object();//方法内的变量引用return obj;
}
foo();//返回的对象不可达,可能会被回收
Object result = foo();//返回的对象赋值了属于强引用
  • 软引用:将对象用SoftReference软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉。软引用可用来实现内存敏感的高速缓存。
public static SoftReference<User> user = new SoftReference<User>(new User());

例如浏览器的后退按钮。按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?

  1. 如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建
  2. 如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出
    这时候软引用就可以实现,在内存充足的时候缓存,不足的时候gc,让程序重新加载页面。
  • 弱引用:将对象用WeakReference软引用类型的对象包裹,弱引用跟没引用差不多,GC会直接回收掉,很少用
public static WeakReference<User> user = new WeakReference<User>(new User());
  • 虚引用:虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系,几乎不用

finalize()方法最终判定对象是否存活

finalize() 是 Java 中 java.lang.Object 类定义的一个方法,它在垃圾回收器准备回收对象之前被调用,可以让对象有机会进行清理操作。

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。
标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。

  1. 第一次标记并进行一次筛选。
    筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,对象将直接被回收。
  2. 第二次标记
    如果这个对象覆盖了finalize方法,会进行第二次标记,如果对象还是应该被回收就真的被回收了。
    缺点与问题:
  • 不确定性:finalize() 什么时候被调用不确定,依赖 GC 时间,可能很晚或根本不调用。
  • 性能开销大:有 finalize() 方法的对象回收会更慢,垃圾回收效率降低。
  • 复活问题:在 finalize() 中可以让对象“复活”(重新被引用),导致难以预测的行为。
  • 可能导致内存泄漏:如果对象在 finalize() 中复活,但不被及时清理。

finalize()方法的运行代价高昂, 不确定性大, 无法保证各个对象的调用顺序, 如今已被官方明确声明为不推荐使用的语法。finalize()方法的运行代价高昂, 不确定性大, 无法保证各个对象的调用顺序, 如今已被官方明确声明为不推荐使用的语法。

如何判断一个类是无用的类

方法区主要回收的是无用的类,那么如何判断一个类是无用的类呢?
类需要同时满足下面4个条件才能算是 “无用的类” :

  • 该类所有的对象实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,没有在任何地方通过反射访问该类的方法。
  • 该类的静态变量/常量没有被外部引用,静态变量/常量持有的强引用会阻止类的卸载,因此静态变量/常量应当不被引用或已置空。

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

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

相关文章

Navicat 技术指引 | TiDB 的 AI 查询交互功能

目前&#xff0c;Navicat 两款工具支持对 TiDB 数据库的管理开发功能&#xff1a;一款是旗舰款 Navicat Premium&#xff0c;另一款是其轻量化功能的 Navicat Premium Lite&#xff08;官方轻量级免费版&#xff09;。Navicat 自版本 17.1 开始支持 TiDB 7。它支持的系统有 Win…

以list为输入条件,查询数据库表,java中的mapper层和mybatis层应该怎么写?

根据一个 List 中的两个字段 rangeCode 和 unitcd&#xff0c;查询数据库表 model_engineering_spatial_unit。这个需求在 Java MyBatis 项目中非常常见&#xff0c;下面我将为你详细写出 Mapper 接口&#xff08;Java&#xff09; 和 MyBatis XML 映射文件 的写法。 ✅ 前提…

pyspark 创建DataFrame

from pyspark.sql import SparkSession from pyspark.sql import StructType, StructField, IntegerType,StringType spark SparkSession.builder.appName(test).getOrCreate() 1、 从列表中创建DataFrame data [(1,"alice"),(2,Blob),(3,Charlie)] columns [&qu…

Vim:从入门到进阶的高效文本编辑器之旅

目录 一、Vim简介 二、Vim的基础操作 2.1 进入和退出Vim 2.2 Vim的三种模式 2.3 基础移动 三、Vim的高效编辑技巧 3.1 文本编辑 3.2 文本删除与修改 3.3 复制与粘贴 四、Vim的进阶使用 4.1 搜索与替换 4.2 寄存器与宏 4.3 插件与配置 五、结语 在编程界&#xff0…

Docker基础理论与阿里云Linux服务器安装指南

文章目录 一、Docker核心概念二、阿里云环境准备三、Docker安装与配置四、核心容器部署示例五、开发环境容器化六、运维管理技巧七、安全加固措施 一、Docker核心概念 容器化本质&#xff1a; 轻量级虚拟化技术&#xff0c;共享主机内核进程级隔离&#xff08;cgroups/namespac…

c#使用笔记之try catch和throw

一、try catch 一种报错的捕捉机制&#xff0c;try块里运行的代码出现错误的时候就会去执行catch块所以一般catch块里都是把错误打印出来或者保存到log日志里&#xff1b; 1.1、具体使用 catch可以用&#xff08;&#xff09;来选择捕捉什么类型的错误&#xff0c;一般用Exc…

(新手友好)MySQL学习笔记(9):索引(常见索引类型,查找结构的发展(二分查找法,二叉搜索树,平衡二叉树,B树,B+树))

目录 索引 常见索引类型 B树 二分查找法 二叉搜索树和平衡二叉树 B树和B树 索引 index&#xff0c;是存储引擎用于快速找到数据的一种数据结构。 MySQL默认使用InnoDB存储引擎&#xff0c;该存储引擎是最重要&#xff0c;使用最广泛的&#xff0c;除非有非常特别的原因需要使用…

进程间通信1(匿名管道)Linux

1 进程间通信的必要性 首先要明确进程间是相互独立的&#xff08;独享一份虚拟地址空间&#xff0c;页表&#xff0c;资源&#xff09;&#xff0c;那怎么样才能使得两个进程间实现资源的发送&#xff1f;所以&#xff0c;两个进程一定需要看到同一份资源&#xff0c;并且⼀个…

CAN2.0、DoIP、CAN-FD汽车协议详解与应用

一、CAN2.0 协议详解与应用示例 1. 技术原理与特性 协议架构&#xff1a;基于 ISO 11898 标准&#xff0c;采用载波监听多路访问 / 冲突检测&#xff08;CSMA/CD&#xff09;机制&#xff0c;支持 11 位&#xff08;CAN2.0A&#xff09;或 29 位&#xff08;CAN2.0B&#xff…

使用nvm管理npm和pnpm

1.使用nvm管理npm // 查看nvm版本 nvm -v // 查看可安装的 node 版本 nvm ls-remote // 安装指定 node 版本 nvm install 24.0.0 // 查看当前已安装的 node 版本及当前使用的版本 nvm list // 使用某个版本 node nvm use 24.0.0 // 卸载指定 node 版本 nvm uninstall 16.20.1…

YOLO11+QT6+Opencv+C++训练加载模型全过程讲解

实现效果&#xff1a; Yolov11环境搭建&#xff08;搭建好的可以直接跳过&#xff09; 最好使用Anconda进行包管理&#xff0c;安装可参考【文章】。下面简单过一下如何快速部署环境。如果搭建过或可以参考其他文章可以跳过Yolo11环境搭建这一章节。总体来说Yolov11环境搭建越…

Python 脚本,用于将 PDF 文件高质量地转换为 PNG 图像

import os import fitz # PyMuPDF from PIL import Image import argparse import logging from tqdm import tqdm# 配置日志 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(PDF2PNG)def convert_pdf_…

【CUDA GPU 支持安装全攻略】PyTorch 深度学习开发者指南

PyTorch 的 CUDA GPU 支持 安装五条铁律&#xff08;最新版 2025 修订&#xff09;&#xff08;适用于所有用户&#xff09;-CSDN博客 是否需要预先安装 CUDA Toolkit&#xff1f;——按使用场景分级推荐及进阶说明-CSDN博客 “100% 成功的 PyTorch CUDA GPU 支持” 安装攻略…

Cyberith 运动模拟器Virtualizer2:提升虚拟现实沉浸体验

奥地利Cyberith公司是一家专注于虚拟现实&#xff08;VR&#xff09;互动解决方案的创新型科技企业&#xff0c;以其研发的Virtualizer虚拟现实步态模拟设备而闻名。该公司的核心技术体现在其设计和制造的全方位跑步机式VR交互平台上&#xff0c;使得用户能够在虚拟环境中实现自…

常见的数据处理方法有哪些?ETL中的数据处理怎么完成

在数字化转型纵深推进的背景下&#xff0c;数据作为新型生产要素已成为驱动企业战略决策、科研创新及智能化运营的核心战略资产。数据治理价值链中的处理环节作为关键价值节点&#xff0c;其本质是通过系统化处理流程将原始观测数据转化为结构化知识产物&#xff0c;以支撑预测…

WHAT - 为甲方做一个官网(二)- 快速版

文章目录 一、明确需求优先级&#xff08;快速决策&#xff09;二、推荐零代码/低代码工具&#xff08;附对比&#xff09;方案1&#xff1a;低代码建站平台&#xff08;适合无技术用户&#xff0c;拖拽式操作&#xff09;方案2&#xff1a;CMS系统&#xff08;适合内容更新频繁…

音视频之H.264视频编码传输及其在移动通信中的应用

系列文章&#xff1a; 1、音视频之视频压缩技术及数字视频综述 2、音视频之视频压缩编码的基本原理 3、音视频之H.264/AVC编码器原理 4、音视频之H.264的句法和语义 5、音视频之H.264/AVC解码器的原理和实现 6、音视频之H.264视频编码传输及其在移动通信中的应用 7、音视…

C#语言入门-task2 :C# 语言的基本语法结构

下面从四个方面对C#的基本语法进行简单介绍&#xff1a; 1. 数据类型 C#的类型可分为值类型和引用类型。值类型变量直接存储数据&#xff0c;引用类型变量则存储对象的引用。 值类型&#xff1a;涵盖整数类型&#xff08;像int、long&#xff09;、浮点类型&#xff08;例如…

c#笔记之类的常量、字段和属性

学习内容: 一、字段 字段是为了对象或者类型存储数据的,可以表达一个对象或者类型的状态;也叫做成员变量;注意字段是在类里面声明的;在方法里声明的是局部变量; 1.1实例字段 用来表示每个实例的状态;比如一个students类;要了解一个学生一般看名字和成绩;所以名字和…

Linux 常用命令(入门)

Linux 常用命令 一、Linux 命令基础 (一)命令格式 Linux 命令的一般格式为:command [-options] [parameter1] … 。其中,command 是命令名,通常是相应功能的英文单词或其缩写;[-options] 是选项,用于对命令进行控制,可省略;parameter1 … 是传给命令的参数,可以是…