Java 入门指南:JVM(Java虚拟机)—— Java 内存运行时的数据区域

news2024/11/16 5:44:59

前言

对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像 C/C++程序开发程序员这样为每一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题。

由于程序员把内存控制权利交给 Java 虚拟机,一旦出现内存泄漏和溢出方面的问题,若不了解虚拟机是怎样使用内存的,排查错误将会是一个非常艰巨的任务。

运行时的数据区域

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域

JDK 1.7

![[Pasted image 20240908211219.png]]

图片来源:JavaGuide.cn

JDK 1.8
![[Pasted image 20240908211205.png]]

Java 虚拟机规范对于运行时数据区域的规定是相当宽松的。以堆为例:堆可以是连续空间,也可以不连续。堆的大小可以固定,也可以在运行时按需扩展 。虚拟机实现者可以使用任何垃圾回收算法管理堆,甚至完全不进行垃圾收集也是可以的。

运行时常量池、方法区、字符串常量池这些都是不随虚拟机实现而改变的逻辑概念,是公共且抽象的

Metaspace、Heap 是与具体某种虚拟机实现相关的物理概念,是私有且具体的

线程私有的区域

线程私有的区域:程序计数器虚拟机栈本地方法栈

程序计数器

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

字节码解释器工作时通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成,从而实现代码的流程控制

另外,为了线程切换后能恢复到正确的执行位置每条线程都需要有一个独立的程序计数器,当线程被切换回来的时候能够知道该线程上次运行到哪儿了。各线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

⚠️ 程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡

Java虚拟机栈

Java 虚拟机栈的生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡

作为 JVM 运行时数据区域的一个核心,除了一些 Native 方法调用是通过本地方法栈实现的,其他所有的 Java 方法调用都是通过栈来实现的(也需要和其他运行时数据区域比如程序计数器配合)。

方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。

栈由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法返回地址

栈帧是方法运行时期十分重要的基础数据结构。和数据结构上的栈类似,两者都是先进后出的数据结构,只支持出栈和入栈两种操作

局部变量表

局部变量表 主要存放了:

  • 编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)

  • 对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
    ![[Pasted image 20231007092408.png]]

操作数栈

操作数栈 主要作为方法调用的中转站使用,用于存放方法执行过程中产生的
中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。

动态链接

动态链接 主要服务一个方法需要调用其他方法的场景。Class 文件的常量池里保存有大量的符号引用比如方法引用的符号引用。当一个方法要调用其他方法,需要 将常量池中指向方法的符号引用转化为其在内存地址中的直接引用

其作用就是为了将符号引用转换为调用方法的直接引用,这个过程也被称为
动态连接

![[Pasted image 20240908212148.png]]

程序运行中栈可能会出现的问题
  • 若栈的内存大小不允许动态扩展,如果函数调用陷入无限循环的话,就会导致栈中被压入太多栈帧而占用太多空间,导致栈空间过深。
    线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。

  • 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常

Java 方法有两种返回方式,一种是 return 语句正常返回,一种是抛出异常。不管哪种返回方式,都会导致栈帧被弹出。栈帧随着方法调用而创建,随着方法结束而销毁。无论方法正常完成还是异常完成都算作方法结束。

以前的 Classic 虚拟机支持栈的动态扩展,
HotSpot虚拟机的栈容量不可以动态扩展 ,所有不会因为栈无法扩展而抛出 OOM 异常,然而在栈空间申请失败时仍会抛出 OOM 异常

本地方法栈

本地方法栈与虚拟机栈的作用相似

虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

本地方法栈保存了本地方法的执行状态,包括本地方法的参数、局部变量和返回结果等信息。通过本地方法栈,JVM能够在本地方法执行期间进行相关操作,如异常处理和垃圾回收等。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowErrorOutOfMemoryError 两种错误。

本地方法栈和Java虚拟机栈并不是完全独立的,它们可以共享一部分内存。但本地方法栈在内存分配和管理上与Java虚拟机栈是独立的

线程共享的区域

线程共享的区域:方法区直接内存(非运行时数据区的一部分)

Java堆

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java 世界中“几乎”所有的对象都在堆中分配,但是,随着 JIT 编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。

从 JDK 1.7 开始已经默认开启 逃逸分析(Escape Analysis),如果 某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存

GC堆

Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代;再细致一点有:EdenSurvivorOld 等空间。进一步划分的目的是更好地回收内存,或者更快地分配内存。

在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为下面三部分:

  1. 新生代内存(Young Generation)
  2. 老生代(Old Generation)
  3. 永久代(Permanent Generation)

下图所示的 Eden 区、两个 SurvivorS0S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。

JDK 1.7 与 JDK 1.8 版本 Hotspot VM 堆结构的对比
![[Pasted image 20231007094506.png]]

图片来源:JavaGuide

JDK 8 版本之后 PermGen(永久代) 已被 Metaspace(元空间) 取代,元空间使用的是本地内存。

大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 S0 或者 S1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1)。当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。

老年代的年龄阈值问题

因为记录年龄的区域在对象头中,这个区域的大小通常是 4 位。这 4 位可以表示的最大二进制数字是 1111,即十进制的 15。因此,对象的年龄被限制为 0 到 15。

老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。若设置的值在 0-15 范围之外,会爆出以下错误:

MaxTenuringThreshold of 20 is invalid; must be between 0 and 15

Hotspot 遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了 survivor 区的一半时,取这个年龄和 MaxTenuringThreshold更小的一个值,作为新的晋升年龄阈值

程序运行中堆的常见错误

堆这里最容易出现的就是 OutOfMemoryError 错误,并且出现这种错误之后的表现形式还会有几种,比如:

  1. java.lang.OutOfMemoryError: GC Overhead Limit Exceeded:当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。

  2. java.lang.OutOfMemoryError: Java heap space :假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。(和配置的最大堆内存有关,且受制于物理内存大小。最大堆内存可通过-Xmx参数配置,若没有特别配置,将会使用默认值)

方法区

方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。在不同的虚拟机实现上,方法区的实现是不同的

当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

  • 类的元数据:包括类名、方法名、字段等信息。
  • 静态变量:类级别的变量。
  • 常量池:包括类或者接口的字面量(Literal)和符号引用(Symbolic References)。
  • 即时编译后的代码:JIT编译器编译后的本地代码缓存。
方法区、永久代、元空间的关系

方法区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区

永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中方法区的两种实现方式。并且,永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现变成了元空间。

在 JDK7 的 HotSpot ,原本放在永久代中的字符串常量值和静态变量等被移出,而到了 JDK8 ,便完全废弃了永久代的概念,改用与 JRockit 、 J9 一样在本地内存实现的元空间(MetaSpace)来替代,并把 JDK7 中永久代还剩余的内容
(主要为类型信息)全部移动到元空间中。

  1. 整个永久代有一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是本地内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。当元空间溢出时会得到如下错误:
   java.lang.OutOfMemoryError: MetaSpace

可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。

  1. 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。

  2. 在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了。

  3. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

方法区常用参数

JDK 1.8 之前永久代还没被彻底移除的时候通常通过下面这些参数来调节方法区大小。

-XX:PermSize=N     //方法区 (永久代) 初始大小
-XX:MaxPermSize=N  //方法区 (永久代) 最大大小,超过这个值将会抛出 OutOfMemoryError 异常:java.lang.OutOfMemoryError: PermGen

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。

JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是本地内存。下面是一些常用参数:

-XX:MetaspaceSize=N     //设置 Metaspace 的初始(和最小大小)
-XX:MaxMetaspaceSize=N //设置 Metaspace 的最大大小

与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存

运行时常量池

Class 文件中除了有类的版本、字段、方法、接口 等描述信息外,还有用于存放编译期生成的各种字面量(Literal)符号引用(Symbolic Reference)常量池表(Constant Pool Table)

  • 字面量是源代码中的固定值的表示法,即通过字面我们就能知道其值的含义。字面量包括整数、浮点数和字符串字面量。

  • 常见的符号引用包括类符号引用字段符号引用方法符号引用
    接口方法符号

  • 常量池表会在类加载后存放到方法区的运行时常量池中。

    运行时常量池的功能类似于传统编程语言的符号表,尽管它包含了比典型符号表更广泛的数据。

    既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 错误。

符号引用 Symbolic References

符号引用以一组符号来描述所引用的目标符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可Symbolic References 与虚拟机实现的内存布局无关,引用的目标不一定是已将加载到虚拟机内存当中的内容。

各种虚拟机实现的内存布局可以各不相同,但它们能接受的符号引用必须一致,Symbolic References 的字面量形式明确定义在《Java 虚拟机规范》的Class文件格式中。

在Class文件中,符号引用以 CONSTANT_Methodref_info
CONSTANT_Class_infoCONSTANT_Fieldred_info 等类型的常量出现。

直接引用 Direct References

直接引用是可以直接指向目标的指针、相对偏移量或一个能间接定位到目标的句柄。直接引用是 和虚拟机实现的内存布局直接相关 的,同一个符号引用在不同虚拟机实例翻译出的直接引用一般不相同。

若有了直接引用,那引用的目标必定已经在虚拟机的内存中存在。

解析

解析阶段是 Java 虚拟机将常量池内的符号引用替换成直接引用的过程。

字符串常量池

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

// 在堆中创建字符串对象”ab“ 
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab"; 
System.out.println(aa==bb);  // true

HotSpot 虚拟机中字符串常量池的实现是 src/hotspot/share/classfile/stringTable.cpp

StringTable 可以简单理解为一个固定大小的 HashTable ,容量为 StringTableSize(可以通过 -XX:StringTableSize 参数来设置),保存的是字符串(key)和 字符串对象的引用(value)的映射关系,字符串对象的引用指向堆中的字符串对象

JDK1.7 之前,字符串常量池存放在永久代。
JDK1.7 字符串常量池和静态变量从永久代移动到了 Java 堆中

JDK 1.6 版本的方法区模型示例

![[Pasted image 20240908213252.png]]

JDK 1.7 版本的方法区模型示例

![[Pasted image 20240908213328.png]]

图片来源:JavaGuide

JDK 1.7 为什么要将字符串常量池移动到堆中

主要是因为永久代(方法区实现)的 GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC 。Java 程序中通常会有大量的被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存

相关问题:JVM 常量池中存储的是对象还是引用呢? - RednaxelaFX - 知乎

直接内存

直接内存是一种特殊的内存缓冲区,并不在 Java 堆或方法区中分配的,而是通过 JNI 的方式在本地内存上分配的

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。

JDK1.4 中新加入的 NIO(Non-Blocking I/O,也被称为 New I/O)(Java NIO 技术详解),引入了一种基于 通道(Channel)缓存区(Buffer) 的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样避免了在 Java 堆和 Native 堆之间来回复制数据,在一些场景下能明显提高性能。

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

类似的概念还有 堆外内存 。在一些文章中将直接内存等价于堆外内存,但这个说法不是特别准确。

堆外内存就是把内存对象分配在新生代+老年代+永久代)以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。

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

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

相关文章

【CSS in Depth 2 精译_025】4.3 弹性布局的方向

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对…

NISP 一级 | 2.3 身份认证

关注这个证书的其他相关笔记:NISP 一级 —— 考证笔记合集-CSDN博客 0x01:身份认证基本方法 身份认证是用户登录系统或网站面对的第一道安全防线,如输入账号口令来登录。身份认证是在网络中确认操作者身份的过程。身份认证一般依据以下三种情…

Thread如何划分为Warp?

1 .Thread如何划分为Warp? https://jielahou.com/code/cuda/thread-to-warp.html Thread Index和Thread ID之间有什么关系呢?(线程架构参考这里:CUDA C Programming Guide (nvidia.com)open in new window) 1维的Thread Index&am…

ORCAD出BOM--位号在同一个Excel格子里

所有相同属性的器件都在同一个格子里 Tools\ Bill of Materials, 注意勾选Open in excel. 勾选Open in excel, 所有相同属性的器件都在同一个格子里 不勾选Open in excel, 5个相同属性的器件都在同一个格子里

代码随想录Day 39|打家劫舍问题,leetcode题目:198.打家劫舍、213.打家劫舍Ⅱ、337.打家劫舍Ⅲ

提示:DDU,供自己复习使用。欢迎大家前来讨论~ 文章目录 题目题目一:198.打家劫舍解题思路: 题目二:213.打家劫舍II解题思路: 题目三: 337.打家劫舍 III解题思路暴力递归记忆化递推动态规划 总结…

Linux基础2-权限2(操作权限,粘滞位,umask,目录文件的rwx权限)

上篇内容:Linux基础2-权限1(用户,权限是什么?)-CSDN博客 目录 一. 权限的操作(命令) 1.1 chmod 1.2 chown 1.3 chgrp 二. 粘滞位 三. umask(遮掩码) 四. 目录文件的 r w x 权限 一. 权限…

数据库的操作:SQL语言的介绍

一.前言 SQL是一种结构化查询语言。关系型数据库中进行操作的标准语言。 二.特点 ①对大小写不敏感 例如:select与Select是一样的 ②结尾要使用分号 没有分号认为还没结束; 三.分类 ①DDL:数据定义语言(数据库对象的操作(结…

服务器重装系统,数据备份 容器备份

文章目录 1.前言2.docker备份2.1 容器备份2.2 镜像备份2.3 数据卷备份 3.docker安装4.jdk安装5.导入镜像6.导入容器 本文档只是为了留档方便以后工作运维,或者给同事分享文档内容比较简陋命令也不是特别全,不适合小白观看,如有不懂可以私信&a…

【最新华为OD机试E卷-支持在线评测】计算疫情扩散时间(200分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

DDComponentForAndroid:探索Android组件化方案

在现代Android应用开发中,随着应用规模的不断扩大,传统的单体应用架构已经无法满足快速迭代和维护的需求。组件化架构作为一种解决方案,可以将应用拆分成多个独立的模块,每个模块负责特定的功能,从而提高代码的可维护性…

2.ChatGPT的发展历程:从GPT-1到GPT-4(2/10)

引言 在人工智能领域,自然语言处理(NLP)是连接人类与机器的重要桥梁。随着技术的不断进步,我们见证了从简单的文本分析到复杂的语言理解的转变。ChatGPT,作为自然语言处理领域的一个里程碑,其发展历程不仅…

【C/C++】C++程序设计基础(继承与派生、多态性)

目录 八、继承与派生8.1 派生类的引入与特性8.2 单继承8.3 同名成员的访问方式8.4 赋值兼容规则8.5 单继承的构造与析构8.6 多继承 九、多态性9.1 运算符重载9.2 虚函数9.3 纯虚函数与抽象类 八、继承与派生 8.1 派生类的引入与特性 -继承:一旦指定了某种事物父代的本质特征&a…

线程相关内容

线程 一、介绍二、thread库1、构造函数(1)函数(2)说明(3)注意 2、join函数3、detach4、joinable函数5、get_id函数 三、mutex的种类1、mutex(1)介绍(2)lock&a…

vant UI之van-tab如何实现标题两行显示

前言: 相必大家在开发移动端或者小程序时都会见到如下设计稿 这个时候大家基本上都会想到使用vant UI 的van-tab组件,如果实现不了那就自己封装一个tab组件这样的情况。 其实使用van-tab是可以实现的,不过要借助van-tab的一系列api和css&…

数据结构(2):LinkedList和链表[1]

下面我们来介绍一种新的数据结构,链表。 我们曾经讨论过顺序表。它的数据存储在物理和逻辑上都是有逻辑的。而我们今天要学习的链表,则在物理结构上非连续存储,逻辑上连续。 1.链表的认识 链表由一个一个的节点组成。 我们可以想象一列火…

乐鑫安全制造全流程

主要参考资料: 【乐鑫全球开发者大会】DevCon24 #10 |乐鑫安全制造全流程 乐鑫官方文档Flash加密: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/security/flash-encryption.html 【ESP32S3】使用 Flash 下载工具完成 Flash 加密功能…

C++ | Leetcode C++题解之第394题字符串解码

题目&#xff1a; 题解&#xff1a; class Solution { public:string src; size_t ptr;int getDigits() {int ret 0;while (ptr < src.size() && isdigit(src[ptr])) {ret ret * 10 src[ptr] - 0;}return ret;}string getString() {if (ptr src.size() || src[…

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目&#xff1a; 题解&#xff1a; static const int MASK1 1 << 7; static const int MASK2 (1 << 7) (1 << 6);bool isValid(int num) {return (num & MASK2) MASK1; }int getBytes(int num) {if ((num & MASK1) 0) {return 1;}int n 0;in…

windows电脑自动倒计时关机

今天聊一聊其他的。我时不时的有一个需求&#xff0c;是关于在windows电脑上定时关机。 不知道怎么地&#xff0c;我好几次都忘了这个自动定时关机的终端命令&#xff0c;于是每一次都要去网上查。 1.鼠标右击【开始菜单】选择【运行】或在键盘上按【 WinR】快捷键打开运行窗口…

【变化检测】基于STANet建筑物(LEVIR-CD)变化检测实战及ONNX推理

主要内容如下&#xff1a; 1、LEVIR-CD数据集介绍及下载 2、运行环境安装 3、STANet模型训练与预测 4、Onnx运行及可视化 运行环境&#xff1a;Python3.8&#xff0c;torch1.12.0cu113 likyoo变化检测源码&#xff1a;https://github.com/likyoo/open-cd 使用情况&#xff1a…