JVM基础详解

news2024/11/18 9:32:33

1.JVM内存结构

1.1 内存结构划分

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

以上代码执行过程:

执行 javac 命令编译源代码为字节码
执行 java 命令

  1. 创建 JVM,调用类加载子系统加载 class,将类的信息存入方法区
  2. 创建 main 线程,使用的内存区域是 JVM 虚拟机栈,开始执行 main 方法代码
  3. 如果遇到了未见过的类,会继续触发类加载过程,同样会存入方法区
  4. 需要创建对象,会使用堆内存来存储对象
  5. 不再使用的对象,会由垃圾回收器在内存不足时回收其内存
  6. 调用方法时,方法内的局部变量、方法参数所使用的是 JVM 虚拟机栈中的栈帧内存
  7. 调用方法时,先要到方法区获得到该方法的字节码指令,由解释器将字节码指令解释为机器码执行
  8. 调用方法时,会将要执行的指令行号读到程序计数器,这样当发生了线程切换,恢复时就可以从中断的位置继续
  9. 对于非 java 实现的方法调用,使用内存称为本地方法栈
  10. 对于热点方法调用,或者频繁的循环代码,由 JIT 即时编译器将这些代码编译成机器码缓存,提高执行性能

定义:
类加载子系统:在运行程序时首次运行类时进行 加载——连接——初始化
可运行数据区: 共享区独占区。共享区主要包括方法区和堆区,独占区包括程序计数器、虚拟机栈和本地方法栈

方法区: 存放已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等
堆: 存放对象、数组、非静态变量
程序计数器: 可以正确控制Java程序中的流程控制,正确轮换多线程
虚拟机栈:每个方法对应一个栈帧,栈帧包含局部变量表、操作数栈、动态链接、方法返回值等
本地方法栈: 不是使用Java实现的函数,用来支持本地方法的调用逻辑的

1.2 内存溢出的区域

  • 不会出现内存溢出的区域——程序计数器

  • 出现 OutOfMemoryError 的情况:

    • 堆内存耗尽 – 对象越来越多,又一直在使用,不能被垃圾回收

    • 方法区内存耗尽 – 加载的类越来越多,很多框架都会在运行期间动态产生新的类

    • 虚拟机栈累积 – 每个线程最多会占用 1 M 内存,线程个数越来越多,而又长时间运行不销毁时

  • 出现 StackOverflowError 的区域:

    • JVM 虚拟机栈,原因有方法递归调用未正确结束、反序列化 json 时循环引用

1.3 方法区,永久区,元空间

定义:
方法区: JVM规范中定义的一块内存区域,用来存储类元数据、方法字节码、即时编译器需要的信息等
永久代:HotSpot虚拟机对JVM规范的实现(JDK1.8之前)
元空间: HotSpot虚拟机对JVM规范的另一种实现(JDK1.8之后),使用本地内存作为这些信息的存储空间

在这里插入图片描述

解释:

  • 当第一次用到某个类的时候,由类加载器将class文件的类元信息读入,并存储于元空间
  • 类元信息是存储于元空间中,无法直接访问
  • 可以用 .class文件间接访问类元信息,它们两属于Java对象,我们在代码中可以使用

在这里插入图片描述

  • 堆内存中:当一个类加载器对象,这个类加载器对象加载的所有类对象,这些类对象对应的所有实例对象都没人引用时,GC就会对它们占用的内存进行释放
  • 元空间中:内存释放以类加载器为单位, 当堆中类加载器内存释放时,对应的元空间中的类元信息也会释放

2. JVM内存参数

2.1 堆内存设置

在这里插入图片描述
具体参数:

-Xms: 最小堆内存( 包括新生代和老年代)
-Xmx: 最大堆内存(包括新生代和老年代)
通常建议将最小堆内存和最大堆内存设置为大小相等,即不需要保留内存,不需要从小到大增长,这样性能较好
-XX:NewSize-XX:MaxNewSize设置新生代的最小与最大值,但一般不建议设置,由JVM自己控制
**-Xmn:**设置新生代大小,相当于同时设置了 -XX:NewSize 与 -XX:MaxNewSize 并且取值相等
图中的保留是指一开始不会占用那么多内存,随着使用内存越来越多,会逐步使用这部分保留内存
按比例设置:
在这里插入图片描述

🔔:
-XX:NewRatio=2:1 表示老年代占两份,新生代占一份
-XX:SurvivorRatio=4:1 表示新生代分成六份,伊甸园占四份,from 和 to 各占一份

2.2 元空间内存设置

在这里插入图片描述

🔔
class space 存储类的基本信息,最大值受 -XX:CompressedClassSpaceSize 控制
non-class space 存储除类的基本信息以外的其它信息(如方法字节码、注解等)
class space 和 non-class space 总大小受 -XX:MaxMetaspaceSize 控制

2.3 代码缓存内存设置

在这里插入图片描述
🔔

  • 如果 -XX:ReservedCodeCacheSize < 240m,所有优化机器代码不加区分存在一起
  • 否则,分成三个区域(图中笔误 mthod 拼写错误,少一个 e)
    • non-nmethods - JVM 自己用的代码
    • profiled nmethods - 部分优化的机器码
    • non-profiled nmethods - 完全优化的机器码

3.JVM垃圾回收

3.1 3种垃圾回收算法

1.标记清除算法:
在这里插入图片描述🔔解释

  • 找到 GC Root 对象,即那些一定不会被回收的对象,如正执行方法内局部变量引用的对象、静态变量引用的对象
  • 标记阶段:沿着 GC Root 对象的引用链找,直接或间接引用到的对象加上标记
  • 清除阶段:释放未加标记的对象占用的内存
    要求:

要点:

  • 标记速度与存活对象线性关系
  • 清除速度与内存大小线性关系
  • 缺点:会产生内存碎片,无法找到足够的连续内存

2.标记整理算法:
在这里插入图片描述
🔔解释:

  • 前面的标记阶段、清理阶段与标记清除法类似
  • 相较之前,多了一步整理的动作,将存活对象向一端移动,可以避免内存碎片的产生

特点:

  • 标记速度与存活对象成线性关系
  • 清除、整理速度与内存大小成线性关系
  • 缺点:移动对象极为负重,必须全程暂停用户应用程序才能进行;性能上较慢

3.标记复制算法:
在这里插入图片描述
🔔解释:

将整个内存分成两个大小相等的区域,from 和 to,其中 to 总是处于空闲,from 存储新创建的对象
标记阶段与前面的算法类似
在找出存活对象后,会将它们从 from 复制到 to 区域,复制的过程中自然完成了碎片整理
复制完成后,交换 from 和 to 的位置即可

特点:
标记与复制速度与存活对象成线性关系
缺点是会占用成倍的空间

3.2 GC与分代回收算法

1) GC的目的: 实现无用的对象内存自动释放,减少内存碎片、加快分配速度

2)GC的要点:

  • 回收区域是堆内存,不包括虚拟机栈

  • 判断无用的对象的方法有可达性分析算法、三色标记法,标记存活的对象,回收未标记的对象

  • GC的具体实现称为垃圾回收器

  • GC 大都采用了分代回收思想(建立在弱分代假说、强分代假说和跨代引用假说之上)

    • 理论依据是大部分对象朝生夕灭,用完立刻就可以回收,另有少部分对象会长时间存活,每次很难回收

    • 根据这两类对象的特性将回收区域分为新生代和老年代,新生代采用标记复制法、老年代一般采用标记整理法

  • 根据 GC的规模可以分成 Minor GC、Mixed GC、Full GC

3)判断无用的对象的方法***

可达性分析算法:从GC Roots为起点开始遍历整个对象图,和GC Roots直接或间接相连的对象才是存活对象,反之就是死亡对象。从GC Roots搜索过的路径叫做引用链。
三色标记法:使用三种颜色表示对象的标记状态,分别是 黑色——已标记,灰色——标记中,白色——未被标记

4)2.分代回收

  1. 伊甸园 eden,最初对象都分配到这里,与幸存区 survivor(分成 from 和 to)合称新生代

在这里插入图片描述
2. 当伊甸园内存不足,标记伊甸园与 from(现阶段没有)的存活对象

在这里插入图片描述
3. 将存活对象采用复制算法复制到 to 中,复制完毕后,伊甸园和 from 内存都得到释放
4. 在这里插入图片描述
5. 将 from 和 to 交换位置
在这里插入图片描述
6. 经过一段时间后伊甸园的内存又出现不足

在这里插入图片描述
7. 标记伊甸园与 from(现阶段没有)的存活对象

在这里插入图片描述

  1. 将存活对象采用复制算法复制到 to 中

  1. 复制完毕后,伊甸园和 from 内存都得到释放
    在这里插入图片描述

  2. 将 from 和 to 交换位置

  3. 老年代 old,当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足或大对象会导致提前晋升)

5)GC 规模

  • Minor GC 发生在新生代的垃圾回收,暂停时间短
  • Mixed GC 对新生代和老年代的部分区域进行垃圾回收,G1垃圾收集器特有
  • Full GC 新生代和老年代完整垃圾回收,暂停时间长,应全力避免

6) 三色标记

即用三种颜色记录对象的标记状态

  • 黑色 – 已标记
  • 灰色 – 标记中
  • 白色 – 还未标记
  1. 起始的三个对象还未处理完成,用灰色表示
  2. 该对象的引用已经处理完成,用黑色表示,黑色引用的对象变为灰色
  3. 依次类推
  4. 沿着引用链都标记了一遍
  5. 最后为标记的白色对象,即为垃圾

3.3 并发漏标问题

比较先进的垃圾回收器都支持并发标记,即在标记过程中,用户线程仍然能工作。但这样带来一个新的问题,如果用户线程修改了对象引用,那么就存在漏标问题。

因此对于并发标记而言,必须解决漏标问题,也就是要记录标记过程中的变化。有两种解决方法

  1. Incremental Update 增量更新法,CMS 垃圾回收器采用
  • 思路是拦截每次赋值动作,只要赋值发生,被赋值的对象就会被记录下来,在重新标记阶段再确认一遍
  1. Snapshot At The Beginning,SATB 原始快照法,G1 垃圾回收器采用
  • 思路也是拦截每次赋值动作,不过记录的对象不同,也需要在重新标记阶段对这些对象二次处理
  • 新加对象会被记录
  • 被删除引用关系的对象也被记录

3.4 垃圾回收器

1.Paraller GC(并行垃圾回收器)

eden 内存不足发生 Minor GC,采用标记复制算法,需要暂停用户线程
old 内存不足发生 Full GC,采用标记整理算法,需要暂停用户线程
注重吞吐量的时候使用这种垃圾回收器

2.ConncurrentMarkSweep GC(CMS垃圾回收器)

工作在 old 老年代,支持并发标记的一款回收器,采用标记清除算法

  • 并发标记时不需暂停用户线程
  • 重新标记仍需暂停用户线程

如果并发失败(回收速度赶不上创建新对象的速度),会触发 Full GC

注重响应时间的时候使用Paraller GC(并行垃圾回收器)

3.G1 GC

将整个堆内存划分为多个大小相等区域,每个区域都可以充当 eden,survivor,old,humongous(专为大对象准备)

分为三个阶段:新生代回收、并发标记、混合收集
1)G1 回收阶段 - 新生代回收

  1. 初始时,所有区域都处于空闲状态

  2. 创建了一些对象,挑出一些空闲区域作为伊甸园区存储这些对象

  3. 当伊甸园需要垃圾回收时,挑出一个空闲区域作为幸存区,用复制算法复制存活对象,需要暂停用户线程

  4. 复制完成,将之前的伊甸园内存释放

  5. 随着时间流逝,伊甸园的内存又有不足

  6. 将伊甸园以及之前幸存区中的存活对象,采用复制算法,复制到新的幸存区,其中较老对象晋升至老年代

  7. 释放伊甸园以及之前幸存区的内存
    2)G1 回收阶段 - 并发标记与混合收集

  8. 当老年代占用内存超过阈值后,触发并发标记,这时无需暂停用户线程

  9. 并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程。这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。此时不会对所有老年代区域进行回收,而是根据暂停时间目标优先回收价值高(存活对象少)的区域(这也是 Gabage First 名称的由来)。
    3.混合收集阶段中,参与复制的有 eden、survivor、old,下图显示了伊甸园和幸存区的存活对象复制
    4.复制完成,内存得到释放。进入下一轮的新生代回收、并发标记、混合收集

如果并发失败,会触发 Full GC
响应时间和吞吐量兼顾

4. 内存溢出

这里举几种典型的导致内存溢出的情况:

  1. 误用线程池导致的内存溢出
  2. 查询数据量太大导致的内存溢出
  3. 动态生成类导致的内存溢出

4.1 误用线程池导致的内存溢出

示例1: 通过Executors自动创建 FixedThreadPool 线程池,代码如下:

代码:

 private static void case1() {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        LoggerUtils.get().debug("begin...");
        while (true){
            executor.submit(() -> {
                try {
                    LoggerUtils.get().debug("send sms...");
                    TimeUnit.SECONDS.sleep(30);
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

运行结果:

17:31:44.144 [main] DEBUG G - begin...
17:31:44.148 [pool-1-thread-1] DEBUG A - send sms...
17:31:44.148 [pool-1-thread-2] DEBUG B - send sms...
 
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"

造成原因:
我们查看源码,可以发现其中使用的工作队列为 LinkedBlockingQueue,它是一个无界的工作队列,任务数量将队列塞满:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

解决办法:

我们在使用Executors自动创建线程的时候尽量不要使用 newFixedThreadPool 这种方式

示例2: 通过 Executors 自动创建带缓存的线程池 (CachedThreadPool),代码如下:

static AtomicInteger c = new AtomicInteger();
 
    private static void case2() {
        ExecutorService executor = Executors.newCachedThreadPool();
        while (true){
            System.out.println(c.incrementAndGet());
            executor.submit(() -> {
                try {
                    TimeUnit.SECONDS.sleep(30);
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

这里测试可以学习视频中,在linux系统中修改进程和线程的最大数来进行测试,这里建议最好不要在Windows下直接测试

造成原因:

我们查看源码,可以发现其中使用的工作队列为 SynchronousQueue ,它创建的线程是没有数量限制的,创建的线程数量过多,耗尽系统的线程资源 :

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

解决办法:

我们在使用Executors自动创建线程的时候尽量不要使用 newCachedThreadPool 这种方式

4.2 查询数据量太大导致的内存溢出

代码:

public class TestOomTooManyObject {
    public static void main(String[] args) {
        //对象本身内存
        long a = ClassLayout.parseInstance(new Product()).instanceSize();
        System.out.println(a);
        // 一个字符串占用内存
        String name = "联想小新Air14轻薄本 英特尔酷睿i5 14英寸全面屏学生笔记本电脑(i5-1135G7 16G 512G MX450独显 高色域)银";
        long b = ClassLayout.parseInstance(name).instanceSize();
        System.out.println(b);
        String desc = "【全金属全面屏】学生商务办公,全新11代处理器,MX450独显,100%sRGB高色域,指纹识别,快充(更多好货)";
        long c = ClassLayout.parseInstance(desc).instanceSize();
        System.out.println(c);
        System.out.println(16 + name.getBytes(StandardCharsets.UTF_8).length);
        System.out.println(16 + desc.getBytes(StandardCharsets.UTF_8).length);
        // 一个对象估算的内存
        long avg = a + b + c + 16 + name.getBytes(StandardCharsets.UTF_8).length + 16 + desc.getBytes(StandardCharsets.UTF_8).length;
        System.out.println(avg);
        // ArrayList 24, Object[] 16 共 40
        System.out.println((1_000_000 * avg + 40) / 1024 / 1024 + "Mb");
    }
 
    static public class Product {
        private int id;
        private String name;
        private int price;
        private String desc;
 
        public int getId() {
            return id;
        }
 
        public void setId(int id) {
            this.id = id;
        }
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public int getPrice() {
            return price;
        }
 
        public void setPrice(int price) {
            this.price = price;
        }
 
        public String getDesc() {
            return desc;
        }
 
        public void setDesc(String desc) {
            this.desc = desc;
        }
    }
}

演示结果:

# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.memory.Universe.getNarrowOopBase()
32
24
24
144
157
381
363Mb

可以看出,占用的内存很大,在高并发的情况下,就有可能出现内存溢出

4.3 动态生成类导致的内存溢出

代码:

public class TestOomTooManyClass {
 
    static GroovyShell shell = new GroovyShell();
 
    public static void main(String[] args) {
        AtomicInteger c = new AtomicInteger();
        while (true) {
            try (FileReader reader = new FileReader("script")) {
 
                shell.evaluate(reader);
                System.out.println(c.incrementAndGet());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

造成原因:

GroovyShell对象无法被回收,导致对象中的 GroovyClassLoader 类加载器无法被回收,导致元空间的内存无法被释放,直至溢出

解决办法:

将静态变量的GroovyShell改为方法中的局部变量,循环完一次对象不再使用就可以回收其内存,然后就可以回收对象中的类加载器,这样就可以回收其所占的内存

5.类加载

5.1 类加载过程的三个阶段

1.加载

  1. 将类的字节码载入方法区,并创建类.class 对象
  2. 如果此类的父类没有加载,先加载父类
  3. 加载是懒惰执行

2.链接

  1. 验证 – 验证类是否符合 Class 规范,合法性、安全性检查
  2. 准备 – 为 static 变量分配空间,设置默认值
  3. 解析 –将常量池的符号引用解析为直接引用

3.初始化

  1. 静态代码块、static 修饰的变量赋值、static final 修饰的引用类型变量赋值,会被合并成一个 方法,在初始化时被调用
  2. static final 修饰的基本类型变量赋值,在链接阶段就已完成
  3. 初始化是懒惰执行

验证手段

  • 使用 jps 查看进程号
  • 使用 jhsdb 调试,执行命令 jhsdb.exe hsdb 打开它的图形界面
    • Class Browser 可以查看当前 jvm 中加载了哪些类
    • 控制台的 universe 命令查看堆内存范围
    • 控制台的 g1regiondetails 命令查看 region 详情
    • scanoops 起始地址 结束地址 对象类型 可以根据类型查找某个区间内的对象地址
    • 控制台的 inspect 地址 指令能够查看这个地址对应的对象详情
  • 使用 javap 命令可以查看 class 字节码

jdk8的类加载器
在这里插入图片描述

5.2 双亲委派机制

所谓的双亲委派,就是指优先委派上级类加载器进行加载,如果上级类加载器

  • 能找到这个类,由上级加载,加载后该类也对下级加载器可见
  • 找不到这个类,则下级类加载器才有资格执行加载

双亲委派的目的有两点

  1. 让上级类加载器中的类对下级共享(反之不行),即能让你的类能依赖到 jdk 提供的核心类

  2. 让类的加载有优先次序,保证核心类优先加载

对双亲委派的误解
下面面试题的回答是错误的

在这里插入图片描述
错在哪了?

  • 自己编写类加载器就能加载一个假冒的 java.lang.System 吗? 答案是不行。

  • 假设你自己的类加载器用双亲委派,那么优先由启动类加载器加载真正的 java.lang.System,自然不会加载假冒的

  • 假设你自己的类加载器不用双亲委派,那么你的类加载器加载假冒的 java.lang.System 时,它需要先加载父类 java.lang.Object,而你没有用委派,找不到 java.lang.Object 所以加载会失败

  • 以上也仅仅是假设。事实上操作你就会发现,自定义类加载器加载以 java. 打头的类时,会抛安全异常,在 jdk9 以上版本这些特殊包名都与模块进行了绑定,更连编译都过不了

6. 4种引用

6.1 强引用

1.普通变量赋值即为强引用,如 A a = new A();

2.通过 GC Root 的引用链,如果强引用不到该对象,该对象才能被回收
在这里插入图片描述

6.2 软引用(SoftReference)

  1. 例如:SoftReference a = new SoftReference(new A());

  2. 如果仅有软引用该对象时,首次垃圾回收不会回收该对象,如果内存仍不足,再次回收时才会释放对象

  3. 软引用自身需要配合引用队列来释放

  4. 典型例子是反射数据
    在这里插入图片描述

6.3 弱引用(WeakReference)

  1. 例如:WeakReference a = new WeakReference(new A());

  2. 如果仅有弱引用引用该对象时,只要发生垃圾回收,就会释放该对象

  3. 弱引用自身需要配合引用队列来释放

  4. 典型例子是 ThreadLocalMap 中的 Entry 对象
    在这里插入图片描述

6.4虚引用(PhantomReference)

  1. 例如: PhantomReference a = new PhantomReference(new A(), referenceQueue);

  2. 必须配合引用队列一起使用,当虚引用所引用的对象被回收时,由 Reference Handler 线程将虚引用对象入队,这样就可以知道哪些对象被回收,从而对它们关联的资源做进一步处理

  3. 典型例子是 Cleaner 释放 DirectByteBuffer 关联的直接内存

在这里插入图片描述

7. finalize

  • 它是 Object 中的一个方法,如果子类重写它,垃圾回收时此方法会被调用,可以在其中进行资源释放和清理工作
  • 将资源释放和清理放在 finalize 方法中非常不好,非常影响性能,严重时甚至会引起 OOM,从 Java9 开始就被标注为 @Deprecated,不建议被使用了

7.1finalize 原理

  1. 对 finalize 方法进行处理的核心逻辑位于 java.lang.ref.Finalizer 类中,它包含了名为 unfinalized 的静态变量(双向链表结构),Finalizer 也可被视为另一种引用对象(地位与软、弱、虚相当,只是不对外,无法直接使用)

  2. 当重写了 finalize 方法的对象,在构造方法调用之时,JVM 都会将其包装成一个 Finalizer 对象,并加入 unfinalized 链表中

  3. Finalizer 类中还有另一个重要的静态变量,即 ReferenceQueue 引用队列,刚开始它是空的。当狗对象可以被当作垃圾回收时,就会把这些狗对象对应的 Finalizer 对象加入此引用队列

  4. 但此时 Dog 对象还没法被立刻回收,因为 unfinalized -> Finalizer 这一引用链还在引用它嘛,为的是【先别着急回收啊,等我调完 finalize 方法,再回收】

  5. FinalizerThread 线程会从 ReferenceQueue 中逐一取出每个 Finalizer 对象,把它们从链表断开并真正调用 finallize 方法

  6. 由于整个 Finalizer 对象已经从 unfinalized 链表中断开,这样没谁能引用到它和狗对象,所以下次 gc 时就被回收了

7.2 finalize 缺点

  • 无法保证资源释放:FinalizerThread 是守护线程,代码很有可能没来得及执行完,线程就结束了
  • 无法判断是否发生错误:执行 finalize 方法时,会吞掉任意异常(Throwable)
  • 内存释放不及时:重写了 finalize 方法的对象在第一次被 gc 时,并不能及时释放它占用的内存,因为要等着 FinalizerThread 调用完 finalize,把它从 unfinalized 队列移除后,第二次 gc 时才能真正释放内存
  • 有的文章提到【Finalizer 线程会和我们的主线程进行竞争,不过由于它的优先级较低,获取到的CPU时间较少,因此它永远也赶不上主线程的步伐】这个显然是错误的,FinalizerThread 的优先级较普通线程更高,原因应该是 finalize 串行执行慢等原因综合导致

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

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

相关文章

[JAVA安全]weblogic反序列化介绍及环境搭建

weblogic反序列化介绍 weblogic是一个常用的web中间件&#xff0c;它的反序列化漏洞也算是比较经典&#xff0c;而在weblogic里面其实反序列化漏洞大致分为两种&#xff0c;一个是基于T3协议的反序列化漏洞&#xff0c;一个是基于XML的反序列化漏洞&#xff0c;以后再说吧&…

用R语言画切线和法线

文章目录7 法线梯度、切线和法线切线和法线的绘制7 法线 梯度、切线和法线 为了书写方便&#xff0c;记xix1,x2,...,xnx_ix_1,x_2,...,x_nxi​x1​,x2​,...,xn​&#xff0c;∂f∂xi∂f∂x1,∂f∂x2,⋯∂f∂xn\frac{\partial f}{\partial x_i}\frac{\partial f}{\partial x_…

springboot+rabbitmq搭建mqtt协议实现订阅发布(亲测9w消息并发)

一、mqtt协议简单介绍 mqtt是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的轻量级通讯协议&#xff0c;通过订阅相应的主题来获取消息&#xff0c;是物联网&#xff08;Internet of Thing&#xff09;中的一个标准传输协议。 二、rabbitmq的安装部署 1. …

c# 随机数,c# 生成随机数,c# 生成区间随机数,c# 生成随机数组

c# 随机数&#xff0c;c# 生成随机数&#xff0c;c# 生成区间随机数&#xff0c;c# 生成随机数组 小试牛刀 先看结果 生成200-700之间的5个随机数 第1的随机数是&#xff1a;647 第2的随机数是&#xff1a;219 第3的随机数是&#xff1a;311 第4的随机数是&#xff1a;210 第5…

Redisson的看门狗机制

背景 据Redisson官网的介绍&#xff0c;Redisson是一个Java Redis客户端&#xff0c;与Spring 提供给我们的 RedisTemplate 工具没有本质的区别&#xff0c;可以把它看做是一个功能更强大的客户端&#xff08;虽然官网上声称Redisson不只是一个Java Redis客户端&#xff09; …

记录Maven的相关操作(笔记整理)

一、安装 我使用的是免安装版的&#xff0c;直接解压缩就可以使用。 二、配置环境变量 打开环境变量配置。右键计算机→属性→高级系统设置→高级→环境变量&#xff0c;在系统变量中配置。 配置MAVEN_HOME。在系统变量中新建&#xff0c;变量名MAVEN_HOME&#xff0c;变量值…

parquet

一、parquet结构 Row Group ​ --Column Chunk&#xff1a;一列对应一个Column Chunk ​ – Page&#xff1a;压缩和编码的单元&#xff0c;parquet的 min/max 索引是针对于page的&#xff0c;存在了文件的页脚。以前的版本是存储Column Chunk和Page的索引&#xff0c;导致在…

Linux命令scp用法

本文主要讲的是scp用法如果哪里不对欢迎指出&#xff0c;主页https://blog.csdn.net/qq_57785602?typeblog首先讲述一下scp用法并不是让你连接公司服务器后用的&#xff08;不是连接公司服务器使用&#xff09;&#xff0c;如果要使用的情况下那么请看下面&#xff1a;winr打开…

【C语言篇】请把这篇文章推给现在还对指针一知半解的童鞋~超生动图解,详细讲解,易懂,易学,让天下没有难懂的指针~

&#x1f331;博主简介&#xff1a;是瑶瑶子啦&#xff0c;一名大一计科生&#xff0c;目前在努力学习C进阶,JavaSE。热爱写博客~正在努力成为一个厉害的开发程序媛&#xff01; &#x1f4dc;所属专栏&#xff1a;C/C ✈往期博文回顾:进入内存,透彻理解数据类型存在的意义,整形…

[java拓展]Mysql数据库的基础指令,和JDBC的使用

1.关于mysql数据库 &#xff08;1&#xff09;概述 DBMS数据库管理系统&#xff0c;用来管理数据库&#xff0c;执行sql语句的东西&#xff0c;Mysql&#xff0c;oracle&#xff0c;sqlite这些严格来说不是数据库而是数据库管理系统&#xff0c;其中Mysql最常用&#xff0c;而…

马来酰亚胺聚乙二醇硅烷,MAL-PEG-Silane 结构,科研试剂溶于大部分有机溶剂

马来酰亚胺聚乙二醇硅烷&#xff0c;MAL-PEG-Silane 中文名称&#xff1a;马来酰亚胺聚乙二醇硅烷 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k。。。 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用…

剑指offer----C语言版----第十九天----面试题25:合并两个有序的链表

目录 1. 合并两个排序的链表 1.1 题目描述 1.2 解题思路 1.3 往期回顾 1. 合并两个排序的链表 原题链接&#xff1a;21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09;1.1 题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个…

new-Crack:StarUML 5.1.0 -2023

StarUML 用于敏捷和简洁建模的复杂软件建模器 当前版本&#xff1a;v5.1.0 主要特征 通用语言2 兼容 UML 2.x 标准元模型和图表&#xff1a;类、对象、用例、组件、部署、复合结构、序列、通信、状态图、活动、时间、交互溢出、信息流和剖面图。 SYSML 支持 支持使用 SysML 图…

SSM03_SpringMVC REST风格 SSM整合

01-SpringMVC简介SpringMVC是隶属于Spring框架的一部分&#xff0c;主要是用来进行Web开发&#xff0c;是对Servlet进行了封装。SpringMVC是处于Web层的框架&#xff0c;所以其主要的作用就是用来接收前端发过来的请求和数据然后经过处理并将处理的结果响应给前端&#xff0c;所…

【iHooya】1月14日寒假集训课作业解析

内部元素之和 输入一个整数矩阵&#xff0c;计算位于矩阵内部的元素之和。所谓矩阵内部的元素&#xff0c;不在第一行和最后一行的元素以及第一列和最后一列的元素。 输入 第一行分别为矩阵的行数m和列数n&#xff08;m < 100&#xff0c;n < 100&#xff09;&#xff0…

Arthas 入门到实战(一)快速入门

Arthas官方文档指出&#xff1a; 介绍&#xff1a;Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&…

人工智能与游戏

游戏是智能应用最好的平台&#xff0c;可惜的是&#xff1a;只用了少部分计算AI&#xff0c;还没有用到智能的计算计1 引言从1950年香农教授提出为计算机象棋博弈编写程序开始&#xff0c;游戏人工智能就是人工智能技术研究的前沿&#xff0c;被誉为人工智能界的“果蝇”&#…

微信小程序070校园食堂订餐多商家带配送

基于微信小程序食堂订餐管理系 系统分为用户和管理员&#xff0c;商家三个角色 用户小程序端的主要功能有&#xff1a; 1.用户注册和登陆小程序 2.查看系统新闻资讯公告 3.用户查看小程序端的菜品信息&#xff0c;在线搜索菜品&#xff0c; 4.用户查看菜品详情&#xff0c;收…

数据结构-二叉搜索树解析和实现

1.含义规则特性二叉搜索树也叫排序二叉树、有序二叉树&#xff0c;为什么这么叫呢&#xff1f;名字由来是什么&#xff1f;主要是它的规则图一规则一&#xff0c;左子树的所有节点的值均小于它的根节点的值规则二&#xff0c;右子树的所有节点的值均大于它的根节点的值&#xf…

摇头测距小车01_舵机和超声波代码封装

目录 一、摇头测距小车图片演示 二、接线方式 三、代码实现 一、摇头测距小车图片演示 就是在小车原有的基础上&#xff0c;在小车前面加一个舵机和一个超声波&#xff0c;把超声波粘在舵机上 二、接线方式 1、超声波接线 VCC-----上官一号5V GND----上官一号GND Trig----…