记一次堆外内存泄漏分析

news2024/9/21 22:37:38

文章目录

      • 1. 背景
      • 2. JVM 内存分布与分析
        • 2.1 JVM 内存分布
        • 2.2 堆外内存泄漏分析思路
        • 2.3 服务器 JVM 参数配置及实际内存分布
        • 2.4 JVM native 内存查看
        • 2.5 手动触发 Full GC
      • 3. 问题排查经历
        • 3.1 定位内存泄漏的位置及初步猜想
          • 1)定位 RES 区域存在内存泄漏
          • 2)分析 RES 内存区域
        • 3.2 解决 Linux glibc 内存碎片问题(66.94.12%下降到64.16%)
        • 3.3 使用 gdb dump并分析RES 内存区域,尝试解决流未关闭问题,未奏效
          • 1)安装 gdb 并 dump 下 RES 内存
          • 2)分析 RES 内存内存,猜测可能流未关闭
        • 3.4 使用 jemalloc 工具进行内存引用分析,并定位问题
          • 1)使用 jemalloc 工具生成内存引用
          • 2)定位 sun.misc.Unsafe#allocateMemory 上层内存分配函数
        • 3.5 原因最终分析与解决方案
          • 1)代码显示调用 Sytem.gc() 释放内存,内存由原来的 64.16%下降到 50.11%
          • 2)压测稳定后,设置接口限流

1. 背景

系统上线前压测发现某个图片分类服务频繁调用时出现:“内存一直不断上涨,到一段时间后逐渐平稳”,内存如下图所示:

在这里插入图片描述

该接口内部逻辑比较简单(示例代码如下):

  • Step 1:先将图片 resize 成算法要求的尺寸 224x224x3
  • Step 2:构造算法分类参数
  • Step 3:算法分类并返回结果
/**
* 功能:对图片对进行分类
* imageUrl:图片 url, 如 http://alicdn.imag.cn/i2/fdsafsdafsfs.png
**/
public Boolean isSizeImage(String imageUrl) {
  // 1. 对图片进行 resize
  float[] content = resize(imageUrl);
  
  // 2. 构造图片分类参数
  ImageClsRequest request = buildImageClsRequest(content);
  
  // 3. 图片分类
  return algorithmClient.predict(request);
}

private float[] resize(String imageUrl){
  // 读取图片
  BufferedImage image = ImageIO.read(imageUrl);
  // 对图片进行 resize
  BufferedImage newImage = new BufferedImage(..);
  // 图片进行重新绘制
  float[] content = new float[224*224*3];
  //.....
  return content;
}

图片的处理很容易产生内存泄漏的问题,通过系统监控发现负载、cpu、jvm 堆内存随着该接口的调用结束而恢复正常,GC频率和范围也在正常范围内,唯有内存上涨后未进行回收, 难道是堆外内存产生了泄漏?

2. JVM 内存分布与分析

2.1 JVM 内存分布

在这里插入图片描述

  • Metaspace:存储被虚拟机加载的类型信息(Java8将方法区的类型信息迁移到 metaspace)。

  • 程序计数器:当前线程所执行的字节码的行号指示器,各线程之间计数器互不影响,独立存储。

  • 本地方法栈: 作用与虚拟机栈发挥作用类似,本地方法栈是为虚拟机使用的 native 方法服务。

    可以认为是 Native 方法相当于 C/C++ 暴露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 方法。当线程调用 Java 方法时,虚拟机会创建一个栈帧并压入 Java 虚拟机栈。然而当它调用的是 native 方法时,虚拟机会保持 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简单地动态连接并直接调用指定的 native 方法(如下图)。

    在这里插入图片描述

  • Code Cache: JVM 将字节码编译成汇编指令时,将这些指令存储在一个称为 Code Cache 的特殊非堆数据区域中。可使用-XX:InitialCodeCacheSize (默认160KB)和 -XX:ReservedCodeCacheSize (默认48MB)调整初始值和最大值。

  • Thread Stack: 所有线程分配的内存大小,可使用 -Xss 调整,64位操作系统中默认1MB。

  • GC算法使用的堆空间和 Compiler 自身操作需要的空间

  • Internal: 如命令行解析器使用的内存、JVMTI等。

  • Direct Memory(直接内存): jdk1.4 引入NIO,NIO 使用Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作,这样能避免在 Java 堆和 Native 堆中来回复制数据。直接内存受本地总内存大小以及处理器寻址空间的限制(可使用 -XX:MaxDirectMemorySize 配置最大内存)。

  • Mapped Memory: 通过系统调用 mmap 函数将某个文件映射到内存中,真正分配在操作系统内核,使用场景如 java 的零拷贝,将减少用户态和内核态的数据复制次数。

  • JNI Memory: 通过 java JNI 调用的 native 方法分配的内存。

附:通过 JVM NMT(native memory tracking)来追踪分析堆外内存,只能 Track JVM 自身的内存分配,第三方的 Native 库内存无法 track,不能 Trace JNI 里直接调用 malloc 时的内存分配,典型场景如 ZipInputStream 场景。

2.2 堆外内存泄漏分析思路

在这里插入图片描述

2.3 服务器 JVM 参数配置及实际内存分布

通过查看机器 jvm 参数如下(机器4c8g):

-Xms4g -Xmx4g #初始堆内存与最大堆内存4GB
-Xmn2g -XX:SurvivorRatio=10 #年轻代大小2GB,Eden 区与 Survivor 区的大小比值为10,即 Eden 区 1706.75MB,两个 Survior 各 170.625MB
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m #元空间256MB,最大512MB
-XX:MaxDirectMemorySize=1g  #直接内存大小 1GB
-XX:+UseConcMarkSweepGC  # 使用CMS作为老年代垃圾回收器

其中 NewRatio 的默认值是 2,即年轻的和老年代的内存比例1:2,但实际上不一定生效。内存实际分布图如下图:
在这里插入图片描述

通过 jmap 命令查看(会自动触发一次 full gc,慎用)内存分布如下:
在这里插入图片描述

2.4 JVM native 内存查看

JVM 具有 Native Memory Tracking 功能,通过 JVM 加上启动参数 -XX:NativeMemoryTracking=detail 可以统计内存使用详细信息。通过以下命令查看 jvm native 内存如下(NMT 的 committed 实际并非都是 JVM 进程已经占用的内存,而是系统分配了这么多内存):

jcmd 1942 VM.native_memory detail scale=MB

在这里插入图片描述

由上图看NMT 并无监控到大量的 native 内存分配的信息( 通过这点可知,NMT 只能追踪到部分 navtive 内存,追踪不到如直接内存等分布)。

2.5 手动触发 Full GC

手动 Full GC 触发前后机器内存并无发生变化,且堆内存表现正常,附常用 Full GC 方式:

 jcmd pid GC.run  # 方式一
 jmap -histo:live <pid> # 方式二 
 jmap -dump:live,file=dump_001.bin PID  # 方式三(jvm在执行该命令前会自动进行一次 full gc)

这时初步猜测: jvm 层面的监控与操作系统层面的监控存在差异,难道是 jvm 已经做回收了,而操作系统层面并无实质释放?

3. 问题排查经历

3.1 定位内存泄漏的位置及初步猜想

1)定位 RES 区域存在内存泄漏

找到 java 进程发现:RES 区域的内存大小(5GB)明显多于正常机器(2.7G),经过计算 RES 区域多的 2.3G 刚好是内存上涨的 29%(因为机器标准是 8G 内存,2.3G/8G=28.75%,监控上看是从37.41%上涨到66.94%)

ps -ef | grep "java"  # 找到运行的 java 服务进程,以1898为例
top -Hp 1898   # 查看该进行下线程内存监控情况(如下图)

在这里插入图片描述
在这里插入图片描述

这里对 VIRT 和 RES 两个区域做下解释:

  • VIRT(virtual memory usage):进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据,以及malloc、new分配的堆空间和分配的栈空间等,另外 VIRT = SWAP + RES,其中 swap 区域是需要内存置换的区域(可参考操作系统虚拟内存原理)。
  • RES(resident memory usage):又称为 RSS,是进程在 RAM 中真正占用的内存大小。RES 包含了它所链接的动态库被加载到物理内存中的内存、栈内存和堆内存。动态链接库占用的内存会被多个进程共享,所以RSS并不能准确反映单进程的内存占用情况。

有人对 RES 区域做了详细的解释如下图:

在这里插入图片描述

即 RSS 内存区域包括了线程堆栈直接内存通过mmap映射的文件JVM字节码等。详细说明参考:https://stackoverflow.com/questions/38597965/difference-between-resident-set-size-rss-and-java-total-committed-memory-nmt

2)分析 RES 内存区域

pmap 命令用于显示进程的内存映射关系,如下图所示,最大块内存使用了4225732KB(4.03GB,正常机器只使用了1.97GB),其中 Address 列表示内存的开始位置, Mapping 列表示占用内存的文件(“anon”表示匿名映射内存)。

匿名映射就是用户空间需要分配一定的物理内存来存储数据,这部分内存不属于任何文件,内核就使用匿名映射将内存中的某段物理地址与用户空间一一映射,这样用户就可用直接操作虚拟地址来范围这段物理内存。比如使用 malloc 申请内存。

pmap -x 1898 | less # 显示进程的内存映射关系

在这里插入图片描述

经过查阅资源发现大多数 RES 区域内存泄漏总是是因 Linux glibc 内存碎片引起的,为了消除心中的疑虑,需要进一步排查是否因 glibc 原因引起。

3.2 解决 Linux glibc 内存碎片问题(66.94.12%下降到64.16%)

glibc 使用内存池为 java 应用通过 malloc 申请堆外内存,当 jvm 回收内存归还操作系统时,操作系统在 free 的时候并不会真正释放内存,而是维护到 glibc 的内存池中供下次使用,从而避免重复申请(当时也有个想法 jvm 和 linux 层面的内存监控不一致,是不是 linux 并未真正释放内存),为了验证想法,去机器上查看使用的 glibc 库,如下图:

在这里插入图片描述

上面看确实是原生的 glibc 版本,为了验证是否能自动释放内存碎片,手动执行调用 malloc_trim 函数:

gdb --batch --pid=1898 -ex "call (int)malloc_trim(0)"

验证结果返回1表明有内存释放(0表示无内存释放),说明存在内存优化空间,经查阅资料操作系统更好的内存管理是 jemalloc,有两种方式进行替换(这里使用的第二种):

  • 方式一:官网下载 jemalloc 安装更换 java 程序链接库即可,实践行得通(jemalloc 文档介绍:http://jemalloc.net/jemalloc.3.html#tuning)
sudo bash -c "sudo su"
sudo yum install git autoconf gcc make
git clone https://github.com/jemalloc/jemalloc
cd jemalloc
sh autogen.sh
./configure --enable-prof
make
make install

export LD_PRELOAD=/usr/local/lib/libjemalloc.so.2
  • 方式二:升级应用启动的 ajdk 版本(新版本使用 jemalloc 来进行内存分配和释放)
# 旧的,底层内存分配使用 glibc
http://***/7/x86_64/current/ali-jdk/ali-jdk-8.4.7-1519273.alios7.x86_64.rpm
# 新的,底层内存分配使用 jemalloc
http://***/7/x86_64/current/ajdk/ajdk-8.10.15_fp13-20210830151435.alios7.x86_64.rpm

经过替换后,如下图所示已经成功替换为 jemalloc:
在这里插入图片描述

再次压测内存从 66.94.12%下降到64.16%,有一定的效果但并不明显,还要进一步排查。

3.3 使用 gdb dump并分析RES 内存区域,尝试解决流未关闭问题,未奏效

1)安装 gdb 并 dump 下 RES 内存
vim /proc/1984/smaps # 查看分配的内存地址空间 6c0000000-7c1f00000
sudo yum install gdb  # 安装 gdb
gdb attach 1984 
dump memory /tmp/fc-01.dump 0x6c0000000 0x700000000 # 分三次进行dump
dump memory /tmp/fc-02.dump 0x700000000 0x7b1f00000
dump memory /tmp/fc-03.dump 0x7b1f00000 0x7c1f00000
strings /tmp/fc-03.dump # 查看内存内容

在这里插入图片描述

2)分析 RES 内存内存,猜测可能流未关闭

通过查看 RES 内存内容(如下图),发现里面很多字节流内容:

在这里插入图片描述

怀疑是不是在进行 http 请求时流未关闭,核心代码如下(左边依赖中间件原代码,右边改造后代码):
在这里插入图片描述

使用 arthas 热部署工具 ArthasHotSwap 修改代码后,再次压测堆外内存上涨问题仍然存在,排除依赖http流未关闭问题。

3.4 使用 jemalloc 工具进行内存引用分析,并定位问题

上面探索无果后,开始深挖堆外内存分配原理,堆外内存的分配一般是通过以下两种方式进行分配:

  • 方式一:java.nio.ByteBuffer#allocateDirect
  • 方式二:sun.misc.Unsafe#allocateMemory

第一种方式使用直接内存,从监控上在整个压测过程只分配2MB左右,忽略不计。所以猜测大部分来自 sun.misc.Unsafe#allocateMemory 分配,为了进一步分析内存分配情况,以下在压测过程中结合 jemalloc 工具来进行具体分析。

1)使用 jemalloc 工具生成内存引用

升级 ajdk 后便自动完成 jemalloc 的安装,这里增加配置每分配1MB内存就自动 dump 内存文件,手动配置如下:

export MALLOC_CONF="prof:true,lg_prof_interval:30,lg_prof_sample:17,prof_prefix:/home/admin/fc/prof_prefix"

上述配置命令参数如下:

  • lg_prof_interval:比如值是 20 表示1MB(2^20),即程序在运行时,每分配(大约)1MB就会 dump 产生一个文件
  • prof:true,表示打开profiling
  • lg_prof_sample:分配样本之间的平均间隔(以log2为基数),以分配活动的字节数衡量。增加采样间隔不仅会降低轮廓保真度,而且会降低计算开销。默认采样间隔为 512 KiB (2^19 B)。
  • prof_prefix:profile dump 文件名前缀。如果前缀设置为空字符串,则不会发生自动转储。示例:jeprof.25851.42.i42.heap

压测过程中生成的 dump 文件如下:

在这里插入图片描述

然后对 dump 文件转换成 svg 图片,再上传到 oss 上下载到本地进行分析(如下图所示):

yum install graphviz
jeprof --svg /***/java jeprof.25959.0.f.heap > 25959.svg # 服务器上生成 svg 图片

在这里插入图片描述

jemalloc 工具分配内存的方式是使用 jemalloc 系统调用函数来分配的对象内存,整个压测过程中jemalloc 分配内存的大小从 220MB 上升到 863MB(上涨643MB),所以有以下结论:

  • sun.misc.Unsafe#allocateMemory:分配内存的主要来源,重点分析
  • com.sun.imageio.plugins.jpeg.JPEGImageReader#readImage:本身不是通过 sun.misc.Unsafe#allocateMemory 分配的内存,可忽略
  • java.util.zip.Inflater#inflateBytes :文件压缩使用,占比不是特别大
2)定位 sun.misc.Unsafe#allocateMemory 上层内存分配函数

由于 sun.misc.Unsafe#allocateMemory 是 JNI 函数,Arthas 和 Btrace 工具都无法对其进行追踪,只得采用本地模拟线上进行不间断请求(另一种方式可使用 arthas 或者 btrace 对 JNI 上游函数进行追踪),结合 JProfiler 插件对 sun.misc.Unsafe#allocateMemory 进行 incoming refrence 分析的方式,引用关系如下图所示:

在这里插入图片描述
在这里插入图片描述

逐一排查(查看哪个类内部调用了 sun.misc.Unsafe#allocateMemory)发现以下三个引用:

  • sun.java2d.pipe.RenderBuffer:图片resize时调用
  • sun.nio.ch.NativeObject:NIO线程分配
  • java.nio.DirectByteBuffer:NIO线程分配

通过本地 debug 方式发现每次调用图片 resize 都会创建 Graphics2D ,同时会创建 GraphicsEnvironment,而在初始化GraphicsEnvironment的实例CGraphicsEnvironment时, 会创建并初始化 OGLRenderQueue,这里一次性分配了32000 byte(约32KB,粗估算了下压测请求共请求约 16888 次,不包含后续内存的写入,该类初始化分配需要的堆外内存约 16888*32/1024MB = 527MB) ,源码如下:

在这里插入图片描述

GraphicsEnvironment为Java应用程序提供了特定平台的GraphicsDevice对象和Font 对象集合。 这些GraphicsDevice可以是各种本机和远端机器的资源,如屏幕、打印机或者是Image Buffer,甚至是Graphics2D绘图方法的目标对象

OGL-specific implementation of RenderQueue. This class provides a single (daemon) thread that is responsible for periodically flushing
the queue, thus ensuring that only one thread communicates with the native OpenGL libraries for the entire process.

3.5 原因最终分析与解决方案

​ 经上述分析产生堆外内存上涨的原因是由于使用 java 原生 jdk 重新绘图引起的,理论上不会存在代码漏洞,所以猜测一次请求(一个线程)使用的堆外内存大小是有限的,压测过程中会不断复用线程的上下文信息,再加上栈帧对垃圾回收的影响(线程变量槽复用,垃圾回收不掉),所以得出结论:给定压力(线程数)情况下,使用的堆外内存是一定,不存在堆外内存泄漏的情况,也就是说在当前压力下,必须要使用这么大的堆外内存。 后来为了验证进行反复压测,发现堆外内存并不会随着压测的次数增加而上涨,而是稳定在一个固定值上。

1)代码显示调用 Sytem.gc() 释放内存,内存由原来的 64.16%下降到 50.11%

​ JNI 申请的内存分配在 jvm 堆外,不受垃圾回收器的管理,需要手动调用 Sytem.gc() 方法来释放内存,但 java 代码中一般不建议直接使用 Sytem.gc() 方法来释放内存,触发 Sytem.gc() 方法会 stop the world,从而影响 java 进程正常工作,这里可以结合 -XX:+ExplicitGCInvokesConcurrent 参数,表示可以进行并发的混合回收,这是 jvm 对 Native memory 的一个优化(jvm参数中不能带 -XX:+DisableExplicitGC)


事实上显式调用 System.gc ,是希望通过 Full GC 来强迫已经无用的 DirectByteMemory 对象释放掉它们关联的 Native Memory,HotSpot VM 只会在 Old GC 的时候才会对 Old 中的对象做 Reference Processing,而在 Young GC 时只会对 Young 里的对象做 Reference Processing。Young 中的 DirectByteBuffer 对象会在 Young GC 时被处理,也就是说,做 CMS GC 的话会对 Old 做 Reference Processing,进而能触发 Cleaner 对已死的 DirectByteBuffer 对象做清理工作。但如果很长一段时间里没做过 GC 或者只做了 Young GC 的话则不会在 Old 触发 Cleaner 的工作,那么就可能让本来已经死亡,但已经晋升到 Old 的 DirectByteBuffer 关联的 Native Memory 得不到及时释放。这几个实现特征使得依赖于 System.gc 触发 GC 来保证 DirectByteMemory 的清理工作能及时完成。

摘自美团技术博客:Java中9种常见的CMS GC问题分析与解决:https://tech.meituan.com/2020/11/12/java-9-cms-gc.html

2)压测稳定后,设置接口限流

为了防止过大流量耗尽机器堆外内存产生 OOM,所以需要对接口限流。

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

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

相关文章

寒假每日一题W1D1——孤独的照片

题目描述 Farmer John 最近购入了 N 头新的奶牛&#xff0c;每头奶牛的品种是更赛牛&#xff08;Guernsey&#xff09;或荷斯坦牛&#xff08;Holstein&#xff09;之一。 奶牛目前排成一排&#xff0c;Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。 然而&…

一本通 1267:【例9.11】01背包问题(详细代码)

经典01背包问题 这里给你3种方法 目录 DFS 思路&#xff1a; 代码&#xff1a; DFS记忆化 思路&#xff1a; 代码&#xff1a; 动态规划 思路&#xff1a; 代码&#xff1a; DFS 时间复杂度 &#xff1a;O(2^n) 思路&#xff1a; DFS求出所有选法&#xff0c;再用…

Maven 之 依赖管理

目录 1、依赖传递 小案例&#xff1a; 2、可选依赖 3、 排除依赖 4、可选依赖和排除依赖的区别 我们开发一个工程需要用到大量得jar包&#xff0c;而这些jar 包就是我们所说得依赖&#xff0c;一个项目可以配置多个依赖。 1、依赖传递 我们来看一下今天用来演示的工程。…

Linux性能学习(1.4):CPU_如何查看CPU上下文切换参数

文章目录1 系统总体上下文参数2 进程的上下文切换参数3 其它参考资料&#xff1a;vmstat&#xff1a;一个标准的报告虚拟内存统计工具 在前面大致了解了上下文切换的相关知识&#xff0c;那么如何在系统中查看上下文切换相关的参数&#xff1f; 1 系统总体上下文参数 使用vm…

人工势场法路径规划算法(APF)

本文主要对人工势场法路径规划算法进行介绍&#xff0c;主要涉及人工势场法的简介、引力和斥力模型及其推导过程、人工势场法的缺陷及改进思路、人工势场法的Python与MATLAB开源源码等方面 一、人工势场法简介 人工势场法是由Khatib于1985年在论文《Real-Time Obstacle Avoidan…

WPF使用触发器需要注意优先级问题

总目录 文章目录总目录前言一、问题开始二、问题说明三、问题订正总结前言 WPF使用触发器需要注意优先级问题 一、问题开始 现在有个需求&#xff1a; 初始状态&#xff08;未选中&#xff09;的时候&#xff0c;CheckBox的Content 为 “乒乓球”&#xff0c;然后选中之后&am…

python机器学习《基于逻辑回归的预测分类》

前言&#xff1a; 本文所有代码均在阿里天池实验室运行&#xff0c;本机的jupyter notebook也可运行。除此之外&#xff0c;还需要导入numpy,matplotlib,sklearn,seaborn包。每期文章前面都会有环境搭建说明。文中的讲解知识点均是按照从上往下讲解&#xff0c;将一些平常未接触…

⼯⼚⽅法模式

⼯⼚⽅法模式 ⼯⼚⽅法模式&#xff0c;属于创建者模式中的一种&#xff0c;这类模式提供创建对象的机制&#xff0c; 能够提升已有代码的灵活性和可复⽤性。 创建者模式包括&#xff1a;⼯⼚⽅法、抽象⼯⼚、⽣成器、原型、单例&#xff0c;这5类。 1.⼯⼚⽅法模式介绍 ⼯⼚…

LaoCat带你认识容器与镜像(二【一章】)

系列二章&#xff0c;祝大家新的一年事事顺心&#xff0c;想要的一定都实现。 本章内容 使用Docker镜像。 本文实操全部基于Ubuntu 20.04 一、使用Docker镜像 镜像&#xff08;image&#xff09;是Docker三大核心概念中最重要的&#xff0c;Docker运行容器前需要本地存在对应得…

在wsl下开发T113的主线linux(5)-构建ubi文件系统

接下来是构建文件系统&#xff0c;这里使用最新的buildroothttps://buildroot.org/download.htmlhttps://buildroot.org/download.html tar xf buildroot-2022.11.tar.gz cd buildroot-2022.11 make menuconfig 配置目标指令集类型 配置外部自定义编译器 配置生成文件系统类型…

数据结构和算法--算法与数据结构的概述、简单排序

目录 算法 算法概述 算法复杂度 数据结构 数据结构的概述 物理结构 逻辑结构 简单排序 1.选择排序 1.1算法描述 1.2算法实现 2冒泡排序 2.1算法描述 2.2算法实现 3插入排序 3.1算法描述 3.2算法实现 三种算法的比较 算法 算法概述 算法是一系列程序指令&am…

回溯算法题型

目录 一组合总和 二组合总和 三子集 四全排列 五解数独 一组合总和 题目描述&#xff1a; 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组…

ArcGIS基础:提取道路中心线

本实验为对道路路面数据进行中心线提取 以路边两侧边界为准&#xff0c;运用等分的办法实现道路中心线提取&#xff0c;原始数据如下所示&#xff08;来源于网络&#xff09;。 道路顶端有一些圆弧段的部分&#xff0c;需要把其去除。 首先要做的是面转线操作&#xff0c;如下…

HashMap解读

1.简介 HashMap &#xff0c;是一种散列表&#xff0c;用于存储 key-value 键值对的数据结构&#xff0c;一般翻译为“哈希表”&#xff0c;提供平均时间复杂度为 O(1) 的、基于 key 级别的 get/put 等操作。 2.哈希表结构 哈希表结构为数组&#xff0c;链表和红黑树。如图 …

已解决+ FullyQualifiedErrorId : UnauthorizedAccess

已解决无法加载文件 E:\day_01\Scripts\activate.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的about_Execution_Policies。 CategoryInfo: SecurityError: &#xff08;:&#xff09; [ ]…

Spring Bean的配置详解

目录 1.bean基础配置 例如&#xff1a;配置UserDaolmpl由Spring容器负责管理 2.Spring开发中主要是对Bean的配置&#xff0c; Bean的常用配置一览如下&#xff1a; 3.bean的别名配置 4.bean作用范围配置 5.bean的实例化 6.bean生命周期 7.Spring的get方法 8.Bean的延迟加载…

57. 数据增广 / 图像增广 代码实现

1. 图像增广 在对常用图像增广方法的探索时&#xff0c;我们将使用下面这个尺寸为400 x 500的图像作为示例。 从github上把img下载下来后&#xff0c;放到同一目录下&#xff1a; d2l.set_figsize() img d2l.Image.open(./img/cat1.jpg) d2l.plt.imshow(img);大多数图像增广…

数字通信系统和模拟通信系统的简单介绍

关于数字和模拟&#xff0c;比较形象的一个对比如下图所示。 模拟系统就好比传统的钟表&#xff0c;秒钟一直在走&#xff0c;也就是连续之意&#xff1b;而数字系统相当于数字表&#xff0c;“ &#xff1a;”的闪烁相当于二进制的 0 和 1&#xff0c;有离散之意。 模拟通信系…

a billion ways to grasp

https://blog.csdn.net/weixin_26752765/article/details/108132661 翻译自 https://darshanhegde.github.io/blog/2020/heuristics-for-robotic-grasping/ 讲述了各种抓取 https://rpal.cse.usf.edu/competition_iros2021/ Grasping is one of the fundamental subtask of a r…

ECCV 2022|DynamicDepth:动态场景下的多帧自监督深度估计

&#x1f3c6;前言&#xff1a;本文别名DynamicDepth (github),如本文的名字所示&#xff0c;本文着重处理的就是动态场景下的多帧自监督深度估计问题。因为MVS在动态场景下会失效&#xff0c;所以在动态区域的多帧深度并不可靠。现在的已有方法例如ManyDepth&#xff0c;利用t…