JVM理解学习

参考视频

运行时数据区

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博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

macbook安装brew出现错误解决办法

我是使用国内的源安装brew的时候&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 我选择了 1: 就出错了&#xff0c;后来切换为2重新安装就好了 安装完成后提示获取不到系统版本&#xff1a; Failed to co…

Linux使用Docker部署Registry结合内网穿透实现公网远程拉取推送镜像

文章目录 1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)…

Linux服务器(Debian系)包含UOS安全相关巡检shell脚本

#!/bin/bash# Define output file current_date$(date "%Y%m%d") # Gets the current date in YYYYMMDD format output_file"server_security_inspection_report_${current_date}.txt"# Empty the file initially echo > $output_file# 获取巡检时间 (…

Hadoop学习1:概述、单体搭建、伪分布式搭建

文章目录 概述基础知识Hadoop组件构成Hadoop配置文件 环境准备配置Hadoop配置下载配置环境变量 Hadoop运行模式Standalone Operation&#xff08;本地&#xff09;官方DemoWordCount单词统计Demo Pseudo-Distributed Operation&#xff08;伪分布式模式&#xff09;配置修改启动…

NCV4275CDT50RKG稳压器芯片中文资料规格书PDF数据手册引脚图图片价格功能

产品概述&#xff1a; NCV4275C 是一款低漏稳压器&#xff0c;可用于严酷汽车环境。它包括了较宽的运行温度范围和输出电压范围。输出调节为 5.0 V 或 3.3 V&#xff0c;额定输出电流为 450 mA。它还提供过电流保护、超温保护和可编程微处理器重置等多种功能。NCV4275C 采用 D…

Python Learn day05

Python Learn day05 本文主要讲解 继承、多态、定制类 继承和多态 什么是继承 当新类想要拥有现有类的功能结构&#xff0c;可以使用继承。继承的前提是新类 is a 现有类&#xff0c;即&#xff1a; 子类 is 父类 总是从某个类继承&#xff1a; class Myclass(object):pass…

Vue+OpenLayers7入门到实战:OpenLayers如何使用全屏控件,来实现地图容器的全屏和退出全屏功能

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7入门到实战 前言 本章介绍如何使用OpenLayers7在地图上使用地图全屏控件,来控制地图容器的全屏和退出全屏的功能。 注意:这里的全屏控件全屏指的是地图容器全屏,并非整个网页全屏。 网页整体全屏和指定网页节点全屏可以参…

十五、计算机视觉-sobel算子

文章目录 前言一、sobel算子的概念二、sobel算子的计算方式三、具体实现 前言 上节课我们学习了梯度的知识&#xff0c;学习了如何去计算梯度&#xff0c;本节我们继续学习计算梯度的方法&#xff0c;本节我们学习使用Sobel算子计算梯度&#xff0c;这与上节课梯度计算方法有所…

ARMv8架构特殊寄存器介绍-0

一、zero 寄存器 零寄存器用作源寄存器时读取零&#xff0c;用作目标寄存器时丢弃结果。您可以在大多数指令中使用零寄存器&#xff0c;但不是所有指令。二、sp寄存器 在ARMv8架构中&#xff0c;要使用的堆栈指针的选择在某种程度上与Exception级别。默认情况下&#xff0c;异…

大数据Doris(六十九):项目线上表现

文章目录 项目线上表现 一、查询响应时间

java学习之路-程序逻辑控制

目录 1.分支结构 1.1 if语句 栗子 判断奇数还是偶数 判断一个年份是否为闰年 1.2switch语句 栗子 2. 循环结构 2.1while 循环 栗子 2.2break和continue break continue 2.3for循环 基本语法 栗子 2.4 do while 循环 3.输入输出 3.1输出 3.2从键盘输入 栗子…

基于FPGA的光纤通信系统的实现的优化技巧与方法

逻辑电路基本框架回顾 跨时钟域同步技术 读写操作相互独立时钟域 A 和 B 不需要一致的相位由专门逻辑控制读写操作的切换 高速数据的乒乓缓存技术

SimpleDateFormat类 --java学习笔记

SimpleDateFormat 代表简单日期格式化&#xff0c;可以用来把日期对象、时间毫秒值格式化成我们想要的形式 常见构造器和方法&#xff1a; pattern 代表需要应用的时间格式—— 时间格式的常见符号&#xff1a; 时间格式的应用举例&#xff1a; import java.text.SimpleDate…

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

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

【计算机网络】UDP/TCP 协议

TCP 协议 一、传输层1. 再谈端口号2. 端口号范围划分3. 进程和端口号4. netstat5. pidof 二、UDP 协议1. UDP 协议端格式(报文)2. UDP 的特点3. 面向数据报4. UDP 的缓冲区 三、TCP 协议1. 认识 TCP2. TCP 协议段格式&#xff08;1&#xff09;4 位首部长度&#xff08;2&#…

Android U pipeline-statusbar

Android U - statusbar pipeline 写在前面 Android原生从T开始对SystemUI进行MVVM改造&#xff0c;U上状态栏部分进行了修改&#xff1b;第一次出现修改不会删除原有逻辑&#xff0c;而是两版并行&#xff0c;留给其他开发者适配的时间&#xff1b;在下一个大版本可能会删除原…

Java获取视频封面图,利用FFmpegFrameGrabber获取视频封面图

依赖 <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.9</version></dependency>传入视频流获取图片byte /*** 获取视频截图** param frameNumber 视频的指定帧数* param …

Ubuntu查看ros版本-linux查看ros版本

使用ros带的rosversion命令即可查看自己的ros版本&#xff1a; rosversion -d

双指针算法_复写零

题目&#xff1a; 给一个固定长度的数组arr&#xff0c;将数组中出现的每一个0都复写一遍&#xff0c;并且将其余元素都往右移动 且不要再超过数组长度的位置写入元素&#xff0c;在数组上直接修改 示例&#xff1a; 双数组模拟操作&#xff1a; 从示例来看&#xff0c;因为…

Linux 中搭建 主从dns域名解析服务器

CSDN 成就一亿技术人&#xff01; 作者主页&#xff1a;点击&#xff01; Linux专栏&#xff1a;点击&#xff01; CSDN 成就一亿技术人&#xff01; ————前言———— 主从&#xff08;Master-Slave&#xff09;DNS架构是一种用于提高DNS系统可靠性和性能的配置方式。…