学习笔记-JAVAJVM-JVM的基本结构及概念

申明:文章内容是本人学习极客时间课程所写,文字和图片基本来源于课程资料,在某些地方会插入一点自己的理解,未用于商业用途,侵删。
原资料地址:课程资料

什么是JVM

原文连接:
原文连接
JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。

在这里插入图片描述

JVM的主要板块

类加载子系统

类加载器的定义(深入理解JVM原话):
通过一个类的全限定名称来描述此类的二进制字节流,将这个动作放到Java虚拟机外部去实现,以便让应用程序自己据欸的那个如何区获取所需要的类,实现这个动作的代码模快称为类加载器。
1 JVM 的类加载是通过ClassLoader及子类来完成的,通常来说有下面几种类加载器:

  • 启动类加载器(Bootstrap ClassLoader)
    负责加载JAVA_ HOME\lib目录的或通过-Xbootclasspath参数指定路径中的且被虚拟机认可(rt.jar) 的类库
  • 扩展类加载器(Extension ClassLoader)
    负责加载JAVA_ _HOME\lib\ext目录或通过java.ext.dirs系统变量指定路径中的类库
  • 应用程序类加载器(Application ClassLoader)
    负责加载用户路径classpath上的类库
  • 自定义类加载器
    加载应用之外的类文件

2 类加载执行顺序
检查顺序是自底向上:加载过程中会先检查类是否被已加载,从Custom到BootStrap逐层检查,只要某个类加载器已加载就视为此类已加载,保证此类所有ClassLoader只加载一 次.
在这里插入图片描述
3 加载时机(检查时自底向上,加载时自顶向下)

1-遇到new、getStatic、 putStatic、 invokeStatic四条指令时。
2-使用java.lang.reflect包方法时,对类进行反射调用。
3-初始化-个类时,发现其父类还没初始化,要先初始化其父类。
4-当虚拟机启动时,用户需要指定–个主类Main,需要先将主类加载。

4 一个类的一生
在这里插入图片描述
5 类加载所做的事情
在类加载的过程中,做了如下几件事情:
1 根据全限定名称加载二进制字节流。
2 将字节流转换为数据结构
3 创建字节码class的对象

6 类加载途径
➢01-jar/war
➢02-jsp生成的class
➢03-数据库中的二进制字节流
➢04-网络中的二进制字节流
➢05-动态代理生成的二进制字节流

在这里插入图片描述
自定义类加载器案例heiloworld

public class CustomClassLoader extends ClassLoader {private final String classPath;public CustomClassLoader(String classPath) {this.classPath = classPath;}public static void main(String[] args) {CustomClassLoader customClassLoader = new CustomClassLoader("E:\\lesson-one\\lesson-one\\src\\lib");try {Class<?> c = customClassLoader.loadClass("com.learn.lessonone.dto.Test");if (c != null) {Object o = c.newInstance();Method say = c.getMethod("say", null);say.invoke(o, null);System.out.println(c.getClassLoader().toString());}} catch (Exception e) {e.printStackTrace();}}@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {try {byte[] calsssDate = getData(name);if (calsssDate != null) {return defineClass(name, calsssDate, 0, calsssDate.length);}} catch (Exception e) {e.printStackTrace();}return super.loadClass(name, resolve);}@Overrideprotected Object getClassLoadingLock(String className) {return super.getClassLoadingLock(className);}public byte[] getData(String className) {String path = classPath + File.separator + className.replace(".", File.separator) + ".class";try (InputStream in = new FileInputStream(path);ByteArrayOutputStream out = new ByteArrayOutputStream()) {byte[] buffer = new byte[1024];int len = 0;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);}} catch (Exception e) {e.printStackTrace();}return null;}
}

类加载机制双亲委派

1-什么是双亲委派?
当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

2-为什么需要双亲委派呢?
双亲委派其实是一种规范,它一定程度上能够保证安全性。就比如我们尝试用的Object,String类,如果我们没有委托父类进行加载,每个子类进行加载,如果这个时候我们自己写了一个类的全限定名称和系统的一模一样,这个时候它加载的就是我们写的类,这样就会导致我们使用的不是Java给我门头提供的Object类,从而程序完全乱套。
为什么双亲委派能够解决这个问题呢,因为我们会一直委托父类去加载,加载Object这种类最终都是由BootstrapClassLoader来加载,它保证了加载的一定是Java提供给我们的Object类,因为BootstrapClassLoader就是负责加载这类内置类的。

3-为什么还需要破坏双亲委派?
在实际应用中,双亲委派解决了Java 基础类统一加载的问题,但是却存在着缺陷。JDK中的基础类作为典型的API被用户调用,但是也存在API调用用户代码的情况,JNDI,SPI,这种情况就需要打破双亲委派模式。
例如:数据库驱动DriverManager。以Driver接口为例,Driver接口定义在]DK中,其实现由各个数据库的服务商来提供由系统类加载器加载。这个时候就需要启动类加载器来委托子类来加载Driver实现,这就破坏了双亲委派。

4-如何破坏双亲委派?
方式一:重写ClassLoader的loadClass方法
方式二:SPl,类委托自类加载器加载Class,以数据库驱动DriverManager为例

自己的理解:
自定义的类始最终都是由ApplicationClassLoader或自定义类加载器加载比如我写了一个CustomObject 继承了Object这个类 并定义了这个类字段结构。在执行加载的时候,CustomObject一直向上委托,最后发现BootStrapClassLoaer加载不了,然后又自顶向下回溯ApplicationClassLoader来加载CustomObject这个类,但是Object BootStrapClassLoaer是能加载的,在回到这里之前Oject已将被加载过了。
类加载的源码 可以类的加载时委托自己的父亲进行加载

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded// 首先检查类是否已被加载Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {// 如果父亲存在则让父亲加载此类c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

运行时数据区概述

按照线程使用情况和职责分成两大类:
1)线程独享(程序执行区)
虚拟机栈,本地方法栈,程序计数器
不需要垃圾回收

2)线程共享(数据存储区域)
堆和方法区
存储类的静态数据和对象数据
需要垃圾回收
在这里插入图片描述

1)堆
是虚拟机所管理的内存中最大的一块,在虚拟器启动时创建,被所有线程共享。此区域的唯一目的就是存放对象实例,一般来说对象实例都是在这里分配内存,包括现对象、数组与运行时常量。
2)堆内存划分依据:
1.弱分代假说:大多数对象存活时间短。
2.强分代假说:熬过越多次的垃圾回收,就越难以被回收。
3.跨代引用假说:跨代引用的对象占少数。
划分情况如下图,1.6:
新生代和老年代,Eden空间,From Survivor, To Survivor。
在这里插入图片描述
·3)划分变迁
在这里插入图片描述
Young 年轻区 :主要保存年轻对象,分为三部分,Eden区、两个Survivor区。
Tenured 年老区 :主要保存年长对象,当对象在Young复制转移一定的次数后,对象就会被转移到Tenured区。
Perm 永久区 :主要保存class、method、filed对象,这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到OOM :
PermGen space 的错误。
Virtual区: 最大内存和初始内存的差值,就是Virtual区。

在这里插入图片描述
由2部分组成,新生代(Eden + 2*Survivor ) + 年老代(OldGen )
JDK1.8中变化最大是,的Perm永久区用Metaspace进行了替换
注意:Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存空间中。区别于JDK1.7
在这里插入图片描述
取消新生代、老年代的物理划分
将堆划分为若干个区域(Region),这些区域中包含了有逻辑上的新生代、老年代区域

下面我们来验证区分划分:
1 我们需要先将不同版本的jdk下载到本地
2 接着借助jdk自带的工具来看JVM的相关信息
在这里插入图片描述
打开这个exe,然后按安装插件
插件下载地址
在这里插入图片描述
然后1.6版本我们可以看到:
在这里插入图片描述
同时我们使用命令

#可以查看堆内存结构,需要Java\bin目录下或者配置环境变量
jmap -heap 10420

在这里插入图片描述
4)虚拟机栈
栈帧(Stack Frame)是用于支持虚拟机进行方法执行的数据结构。
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行
完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程。
栈内存为线程私有的空间,每个线程都会创建私有的栈内存,生命周期与线程相同,每个Java方法在执
行的时候都会创建一个栈帧(Stack Frame)。栈内存大小决定了方法调用的深度,栈内存过小则会导
致方法调用的深度较小,如递归调用的次数较少。

虚拟机栈可能出现的两种异常:
如果线程请求的栈深度大于虚拟机所允许的深度(Xss默认1m),会抛出StackOverflowError异常.
下面这个例子中,我们大约递归到2完多次就会堆栈溢出。

public class DemoError {private static int cnt = 0;private  void call() {cnt ++;try {call();} catch (Throwable e) {e.printStackTrace();System.out.println(cnt);}}public static void main(String[] args) {DemoError demoError = new DemoError();demoError.call();}
}
#空间进一步缩小,递归的深度会进一步缩小
java -Xss256k DemoError 

如果在创建新的线程时,没有足够的内存去创建对应的虚拟机栈,会抛出OutOfMemoryError异常。
5)本地方法栈
本地方法栈和虚拟机栈相似,区别就是虚拟机栈为虚拟机执行Java服务(字节码服务),而本地方法栈为虚拟机使用到的Native方法(比如C++方法)服务。

6)方法区
方法区(Method Area)是可供各个线程共享的运行时内存区域,方法区本质上是Java语言编译后代码存储区域,它存储每一个类的结构信息,例如:运行时常量池、成员变量、方法数据、构造方法和普通方法的字节码指令等内容。很多语言都有类似区域。
方法区的具体实现有两种:永久代(PermGen)、元空间(Metaspace)
方法区存储下面这几种数据类型:
第一:Class

  1. 类型信息,比如Class(com.hero.User类)
  2. 方法信息,比如Method(方法名称、方法参数列表、方法返回值信息)
  3. 字段信息,比如Field(字段类型,字段名称需要特殊设置才能保存的住)
  4. 类变量(静态变量):JDK1.7之后,转移到堆中存储
  5. 方法表(方法调用的时候) 在A类的main方法中去调用B类的method1方法,是根据B类的方
    法表去查找合适的方法,进行调用的。
    第二:运行时常量池(字符串常量池):从class中的常量池加载而来,JDK1.7之后,转移到堆中存

    字面量类型
    引用类型–>内存地址
    第三:JIT编译器编译之后的代码缓存

永久代和元空间的区别是什么?
1)JDK1.8之前使用的方法区实现是永久代,JDK1.8及以后使用的方法区实现是元空间。
2)存储位置不同:
永久代所使用的内存区域是JVM进程所使用的区域,它的大小受整个JVM的大小所限制。
元空间所使用的内存区域是物理内存区域。那么元空间的使用大小只会受物理内存大小的限
制。
3)存储内容不同:
永久代存储的信息基本上就是上面方法区存储内容中的数据。
元空间只存储类的元信息,而静态变量和运行时常量池都挪到堆中。
3)为什么要使用元空间来替换永久代?
字符串存在永久代中,容易出现性能问题和永久代内存溢出。

  • 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代
  • 溢出,太大则容易导致老年代溢出。
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  • Oracle 计划将HotSpot 与 JRockit 合二为一。
  • 在这里插入图片描述
    下面来看几个例子搞明白这个变迁:
import java.util.ArrayList;
import java.util.List;
public class StringDemo {public static void main(String[] args) {String base = "";List<String> strings = new ArrayList<String>();for (int i = 0; i < Integer.MAX_VALUE; i ++) {String str =  base  += "abcdefg";strings.add(str.intern());}}
}
java -XX:PermSize=8m -XX:MaxPermSize=8m StringDemo

在1.6的运行结果:
在这里插入图片描述
1.7运行结果:
永久带还保留但是字符串常量已经存到了堆中去,因为报的是堆内存溢出
在这里插入图片描述

1.8的运行结果:
可以看到1.8永久带彻底被移除,字符串常量存储到了堆内存中。
在这里插入图片描述

  1. 运行时常量

三种常量池的比较

class常量池:一个class文件只有一个class常量池
字面量:数值型(int、float、long、double)、双引号引起来的字符串值等
符号引用:Class、Method、Field等

运行时常量池:一个class对象有一个运行时常量池
字面量:数值型(int、float、long、double)、双引号引起来的字符串值等
符号引用:Class、Method、Field等

字符串常量池:全局只有一个字符串常量池
双引号引起来的字符串值

我们定义字符串的时候如果直接用""进行引用则是存储在字符串常量池中,如果用new String()则是存储在堆中。字符串常量池底层是一个拉链哈希表,用来存储和索引,所以字符串常量池不会存储重复的字符串。
看下面这几个例子来理解:
例子1

String a = "abc";
String b = "abc";
// true 我们知道 == 比较的是存储地址,所以我们可以看到这两个字符串都是存储在字符串常量池中的,并且引用的是同一个地址,那么肯定是只存储了一份。
System.out.println(a == b);

例子2

String a = "abc";
String b =  new String("abc");
// false b 这个对象是通过new出来的,他不会放到字符串常量池中
System.out.println(a == b);

例子3

String a = "abc";
String c = "def";
String d = a + c;
String f = "abcdef";
// false 所以只有"" 的字符串在在字符串常量池,涉及到任何其它操作都不会存储在字符串常量池中
System.out.println(f == d);
// intern将字符串移动到字符串常量池中
// 因为我们调用函数intern d 的引用移动了字符串常量池,他们的地址也就一样了
System.out.println(f == d.intern());

例子4 map的底层实现和字符串常量池基本一样

Map<String, Integer> map = new HashMap<>();
map.put("通话", 51);
map.put("重地", 55);
// 两个字符串的hashcode一模一样
System.out.println("通话".hashCode());
System.out.println("重地".hashCode());
// hashcode 相同的时候会进行拉链,也就是先比较hashcode如果相同在比较值,最后放到这个槽的链表上
System.out.println(map.size());

8)程序计数器
程序计数器(Program Counter Register),也叫PC寄存器,是一块较小的内存空间,它可以看作是当
前线程所执行的字节码指令的行号指示器。字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支,循环,跳转,异常处理,线程回复等都需要依赖这个计数器来完
成。
为什么需要程序计数器?
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(针对多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线
程切换(系统上下文切换)后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
存储的什么数据?
如果一个线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,这个计数器的值则为空。
异常:此内存区域是唯一一个在Java的虚拟机规范中没有规定任何OutOfMemoryError异常情况的区
域。
9)直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,
它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象
作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制。

直接内存(堆外内存)与堆内存比较
直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显
下面这个例子可以比较它们读写之前的差异:

public class ByteBufferCompare {public static void main(String[] args) {//allocateCompare(); //分配比较operateCompare(); //读写比较}/*** 直接内存 和 堆内存的 分配空间比较* 结论: 在数据量提升时,直接内存相比非直接内的申请,有很严重的性能问题*/public static void allocateCompare() {int time = 1000 * 10000; //操作次数,1千万long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {//ByteBuffer.allocate(int capacity) 分配一个新的字节缓冲区。//非直接内存分配申请ByteBuffer buffer = ByteBuffer.allocate(2);}long et = System.currentTimeMillis();System.out.println("在进行" + time + "次分配操作时,堆内存 分配耗时:" +(et - st) + "ms");long st_heap = System.currentTimeMillis();for (int i = 0; i < time; i++) {//ByteBuffer.allocateDirect(int capacity) 分配新的直接字节缓冲区。//直接内存分配申请ByteBuffer buffer = ByteBuffer.allocateDirect(2);}long et_direct = System.currentTimeMillis();System.out.println("在进行" + time + "次分配操作时,直接内存 分配耗时:" +(et_direct - st_heap) + "ms");}/*** 直接内存 和 堆内存的 读写性能比较* 结论:直接内存在直接的IO 操作上,在频繁的读写时 会有显著的性能提升*/public static void operateCompare() {int time = 10 * 10000 * 10000; //操作次数,10亿ByteBuffer buffer = ByteBuffer.allocate(2 * time);long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {// putChar(char value) 用来写入 char 值的相对 put 方法buffer.putChar('a');}buffer.flip();for (int i = 0; i < time; i++) {buffer.getChar();}long et = System.currentTimeMillis();System.out.println("在进行" + time + "次读写操作时,非直接内存读写耗时:" +(et - st) + "ms");ByteBuffer buffer_d = ByteBuffer.allocateDirect(2 * time);long st_direct = System.currentTimeMillis();for (int i = 0; i < time; i++) {// putChar(char value) 用来写入 char 值的相对 put 方法buffer_d.putChar('a');}buffer_d.flip();for (int i = 0; i < time; i++) {buffer_d.getChar();}long et_direct = System.currentTimeMillis();System.out.println("在进行" + time + "次读写操作时,直接内存读写耗时:" +(et_direct - st_direct) + "ms");}
}

从数据流的角度,来看:

非直接内存作用链:本地IO –>直接内存–>非直接内存–>直接内存–>本地IO
直接内存作用链:本地IO–>直接内存–>本地IO
直接内存的使用场景:
有很大的数据需要存储,它的生命周期很长
适合频繁的IO操作,例如:网络并发场景

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

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

相关文章

谷歌推出AI模型机器人RT2 将文本和图像输出为机器人动作

去年年底&#xff0c;ChatGPT火遍全球&#xff0c;全世界都见识了大语言模型的强大力量。人们对大模型不再陌生&#xff0c;开始使用基于大模型的应用绘画、作图、搜索资料、设计剧情等&#xff0c;而妙用不止于此。谷歌推出了Robotics Transformer 2(RT2)&#xff0c;这是一个…

分布式 - 消息队列Kafka:Kafka生产者架构和配置参数

文章目录 1. kafka 生产者发送消息整体架构2. Kafka 生产者重要参数配置01. acks02. 消息传递时间03. linger.ms04. buffer.memory05. batch.size06. max.in.flight.requests.per.connection07. compression.type08. max.request.size09. receive.buffer.bytes和 send.buffer.b…

【PyQt5+matplotlib】获取鼠标在canvas上的点击坐标

示例代码&#xff1a; import sys import matplotlib.pyplot as plt from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvasclass MyMainWindow(QMainWindow):de…

UE4 像素流 学习笔记

使用场景&#xff1a; 1、登录服务器&#xff0c;服务器上安装node.js Download | Node.js (nodejs.org) 点击该网址 点击Windows Installer 2、登录服务器&#xff0c;拷贝本地UE Pixel Streaming包到服务器 启用插件后重启该项目 然后打包 打包成功过后创建快捷方式 将该I…

SDU Crypto School - 计算不可区分性1

Encryption: Computational security 1-4 主讲人&#xff1a;李增鹏&#xff08;山东大学&#xff09; 参考教材&#xff1a;Jonathan Katz, Yehuda Lindell, Introduction to Modern Cryptography - Principles and Protocols. 什么是加密 首先&#xff0c;加密方案的目的在于…

网络:CISCO、Huawei、H3C命令对照

思科、华为、锐捷命令对照表 编号思科华为锐捷命令解释1 2writesavesave保存3456 如果你所处的视图为非系统视图&#xff0c;需要查看配置的时候&#xff0c;需要在该配置命令前加do。 在特定的视图之下&#xff0c;有对应的特定命令。例如&#xff0c;在接口视图下的ip addre…

Ubuntu18.04使用carla0.9.5联合仿真搭环境报错

Ubuntu18.04使用工程与carla0.9.5联合仿真报错 1 File "/home/cg/Auto_driving/src/ros-bridge/carla_ros_bridge/src/carla_ros_bridge/client.py", line 18, in <module>from carla_ros_bridge.bridge_with_rosbag import CarlaRosBridgeWithBagFile "…

[保研/考研机试] KY180 堆栈的使用 吉林大学复试上机题 C++实现

题目链接&#xff1a; 堆栈的使用_牛客题霸_牛客网 描述 堆栈是一种基本的数据结构。堆栈具有两种基本操作方式&#xff0c;push 和 pop。其中 push一个值会将其压入栈顶&#xff0c;而 pop 则会将栈顶的值弹出。现在我们就来验证一下堆栈的使用。 输入描述&#xff1a; 对于…

Zabbix自动注册服务器及部署代理服务器

文章目录 一.zabbix自动注册1.什么是自动注册2.环境准备3.zabbix客户端配置4.在 Web 页面配置自动注册5.验证自动注册 二.部署 zabbix 代理服务器1.分布式监控的作用&#xff1a;2.环境部署3.代理服务器配置4.客户端配置5.web页面配置5.1 删除原来配置5.2 添加代理5.3 创建主机…

c语言——三子棋

基本框架 三个文件: 其中.cpp文件用于游戏具体函数设计&#xff0c;.h文件为游戏的函数声明&#xff0c;test.cpp文件用于测试游戏运行。 需要用到的头文件&#xff1a; #include <stdio.h> #include <stdlib.h>//rand&srand #include <time.h>//时间相…

[oeasy]python0083_[趣味拓展]字体样式_正常_加亮_变暗_控制序列

字体样式 回忆上次内容 上次了解了 一个新的转义模式 \033 逃逸控制字符 esc esc 让输出 退出 标准输出流进行 控制信息的设置 可以 清屏也可以 设置光标输出的位置 还能做什么呢&#xff1f; 可以 设置 字符的颜色吗&#xff1f;&#xff1f;&#xff1f;&#x1f914; 查…

利用Simulink Test进行模型单元测试 - 1

1.搭建用于测试的简单模型 随手搭建了一个demo模型MilTestModel&#xff0c;模型中不带参数 2.创建测试框架 1.模型空白处右击 测试框架 > 为‘MilTestModel’创建 菜单 2.在创建测试框架对话框中&#xff0c;点击OK&#xff0c;对应的测试框架MilTestMode_Harness1就自动…

第五次作业 运维高级 构建 LVS-DR 集群和配置nginx负载均衡

1、基于 CentOS 7 构建 LVS-DR 群集。 LVS-DR模式工作原理 首先&#xff0c;来自客户端计算机CIP的请求被发送到Director的VIP。然后Director使用相同的VIP目的IP地址将请求发送到集群节点或真实服务器。然后&#xff0c;集群某个节点将回复该数据包&#xff0c;并将该数据包…

Android Jetpack

Jetpack 是一个由多个库组成的套件&#xff0c;可帮助开发者遵循最佳实践、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码&#xff0c;让开发者可将精力集中于真正重要的编码工作。 1.基础组件 &#xff08;1&#xff09;AppCompat&#xff1a;使得支持较低…

Qt画波浪球(小费力)

画流动波浪 #ifndef WIDGET3_H #define WIDGET3_H#include <QWidget> #include <QtMath> class widget3 : public QWidget {Q_OBJECT public:explicit widget3(QWidget *parent nullptr);void set_value(int v){valuev;}int get_value(){return value;} protecte…

vue中vuex的五个属性和基本用法,另加js-cookie的使用

VueX 是一个专门为 Vue.js 应用设计的状态管理构架&#xff0c;统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。 Vuex有五个核心概念&#xff1a; state, getters, mutations, actions, modules。 1. state&#xff1a; vuex的基本数据&…

【Linux】-- 进程间通信

目录 一、进程间通信介绍 二、管道 1.什么是管道&#xff08;pipe&#xff09; 2.重定向和管道 &#xff08;1&#xff09;为什么要有管道的存在 &#xff08;2&#xff09;重定向和管道的区别 3.匿名管道 &#xff08;1&#xff09;匿名管道原理 &#xff08;2&…

SpringCloud实用篇5——elasticsearch基础

目录 1.初识elasticsearch1.1 了解ES1.1.1 elasticsearch的作用1.1.2 ELK技术栈1.1.3 elasticsearch和lucene1.1.4 总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3 es的一些概念1.3.1 文档和字段1.3.2 索引和映射1.3.3 mysql与elasticsearch 1.4 部署单点…

直播招聘小程序解决方案

项目开发愿景 介绍工作拿佣金&#xff0c;Boss直播现真身。做为直播招聘的新平台&#xff0c;让求职和招聘变得更简单&#xff01;企业发布招聘视频&#xff0c;展现公司环境与实力&#xff0c;开通会员可以直播招聘、在线面试功能&#xff1b;求职者刷视频可以刷到工作…