一文理解JVM虚拟机

news2024/11/13 13:18:48

一. JVM内存区域的划分

1.1 java虚拟机运行时数据区

java虚拟机运行时数据区分布图:
在这里插入图片描述

  • JVM栈(Java Virtual Machine Stacks): Java中一个线程就会相应有一个线程栈与之对应,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈,因此栈存储的信息都是跟当前线程(或程序)相关信息的,包括局部变量、程序运行状态、方法返回值、方法出口等等。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  • 堆(Heap): 堆是所有线程共享的,主要是存放对象实例和数组。处于物理上不连续的内存空间,只要逻辑连续即可
  • 方法区(Method Area): 属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
  • 常量池(Runtime Constant Pool): 它是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
  • 本地方法栈(Native Method Stacks):
    其中,堆(Heap)JVM栈是程序运行的关键,因为:
    1、栈是运行时的单位(解决程序的运行问题,即程序如何执行,或者说如何处理数据),而堆是存储的单位(解决的是数据存储的问题,即数据怎么放、放在哪儿)。
    2、堆存储的是对象。栈存储的是基本数据类型和堆中对象的引用;(参数传递的值传递和引用传递)

那为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?

1、从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据,分工明确,处理逻辑更为清晰体现了“分而治之”以及“隔离”的思想。
2、堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这样共享的方式有很多收益:提供了一种有效的数据交互方式(如:共享内存);堆中的共享常量和缓存可以被所有栈访问,节省了空间。
3、栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
4、堆和栈的结合完美体现了面向对象的设计。当我们将对象拆开,你会发现,对象的属性即是数据,存放在堆中;而对象的行为(方法)即是运行逻辑,放在栈中。因此编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻辑。

1.2 堆(Heap)和JVM栈:

1.2.1 堆(Heap)

Java堆是java虚拟机所管理内存中最大的一块内存空间,处于物理上不连续的内存空间,只要逻辑连续即可,主要用于存放各种类的实例对象。该区域被所有线程共享,在虚拟机启动时创建,用来存放对象的实例,几乎所有的对象以及数组都在这里分配内存(栈上分配、标量替换优化技术的例外)。
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor(S0)、To Survivor(S1)。如图所示:
在这里插入图片描述
 这样划分的目的是为了使jvm能够更好的管理内存中的对象,包括内存的分配以及回收。 而新生代按eden和两个survivor的分法,是为了

  • 有效空间增大,eden+1个survivor;
  • 有利于对象代的计算,当一个对象在S0/S1中达到设置的
  • XX:MaxTenuringThreshold值后,会将其挪到老年代中,即只需扫描其中一个survivor。如果没有S0/S1,直接分成两个区,该如何计算对象经过了多少次GC还没被释放。
  • 两个Survivor区可解决内存碎片化

1.2.2 堆栈相关的参数

参数描述
-Xms堆内存初始大小,单位m、g
-Xmx堆内存最大允许大小,一般不要大于物理内存的80%
-Xmn年轻代内存初始大小
-Xss每个线程的堆栈大小,即JVM栈的大小
-XX:NewRatio年轻代(包括Eden和两个Survivor区)与年老代的比值
-XX:NewSzie(-Xns)年轻代内存初始大小,可以缩写-Xns
-XX:MaxNewSize(-Xmx)年轻代内存最大允许大小,可以缩写-Xmx
-XX:SurvivorRatio年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1
-XX:MinHeapFreeRatioGC后,如果发现空闲堆内存占到整个预估堆内存的40%,则放大堆内存的预估最大值,但不超过固定最大值。
-XX:MaxHeapFreeRatio预估堆内存是堆大小动态调控的重要选项之一。堆内存预估最大值一定小于或等于固定最大值(-Xmx指定的数值)。前者会根据使用情况动态调大或缩小,以提高GC回收的效率,默认70%
-XX:MaxTenuringThreshold垃圾最大年龄,设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
-XX:InitialTenuringThreshold可以设定老年代阀值的初始值
-XX:+PrintTenuringDistribution查看每次minor GC后新的存活周期的阈值

每次GC 后会调整堆的大小,为了防止动态调整带来的性能损耗一般设置-Xms、-Xmx 相等。
新生代的三个设置参数:-Xmn,-XX:NewSize,-XX:NewRatio的优先级:
(1)最高优先级: -XX:NewSize=1024m和-XX:MaxNewSize=1024m
(2)次高优先级: -Xmn1024m (默认等效效果是:-XX:NewSize==-XX:MaxNewSize==1024m)
(3)最低优先级:-XX:NewRatio=2
推荐使用的是-Xmn参数,原因是这个参数很简洁,相当于一次性设定NewSize和MaxNewSIze,而且两者相等。

1.3 jvm对象

1.3.1 创建对象的方式

在这里插入图片描述
各个方式的实质操作如下:

方式实质
使用new关键调用无参或有参构造器函数创建
使用Class的newInstance方法调用无参或有参构造器函数创建,且需要是publi的构造函数
使用Constructor类的newInstance方法调用有参和私有private构造器函数创建,实用性更广
使用Clone方法不调用任何参构造器函数,且对象需要实现Cloneable接口并实现其定义的clone方法,且默认为浅复制
第三方库Objenesis利用了asm字节码技术,动态生成Constructor对象

1.3.2 jvm对象分配

在虚拟机层面上创建对象的步骤:
在这里插入图片描述

步骤解析
1、判断对象对应的类是否加载、链接、初始化虚拟机遇到一条new指令,首先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化。如果没有,那么必须先执行类的加载、解释、初始化(类的clinit方法)。
2、为对象分配内存类加载检查通过后,虚拟机为新生对象分配内存。对象所需内存大小在类加载完成后便可以完全确定,为对象分配空间无非就是从Java堆中划分出一块确定大小的内存而已。
3、处理并发安全问题另外一个问题及时保证new对象时候的线程安全性:创建对象是非常频繁的操作,虚拟机需要解决并发问题。 虚拟机采用了两种方式解决并发问题:(1)CAS配上失败重试的方式保证指针更新操作的原子性;(2)TLAB 把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲区,(TLAB ,Thread Local Allocation Buffer)虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。
4、初始化分配到的空间内存分配结束,虚拟机将分配到的内存空间都初始化为零值(不包括对象头)。这一步保证了对象的实例字段在Java代码中可以不用赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值
5、设置对象的对象头将对象的所属类(即类的元数据信息)、对象的HashCode和对象的GC分代年龄等数据存储在对象的对象头中
6、执行init方法进行初始化在Java程序的视角看来,初始化才正式开始,开始调用方法完成初始赋值和构造函数,所有的字段都为零值。因此一般来说(由字节码中是否跟随有invokespecial指令所决定),new指令之后会接着就是执 行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全创建出来。

1.3.3 对象分配内存方式

分配对象内存,有两种分配方式,指针碰撞和空闲列表:
(1)如果内存是规整的,那么虚拟机将采用的是指针碰撞法(Bump The Pointer)来为对象分配内存。意思是所有用过的内存在一边,空闲的内存在另外一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把指针向空闲那边挪动一段与对象大小相等的距离罢了。
如果垃圾收集器选择的是Serial、ParNew这种基于压缩算法的,虚拟机采用这种分配方式。
一般使用带有compact(整理)过程的收集器时,使用指针碰撞。
(2)如果内存不是规整的,已使用的内存和未使用的内存相互交错,那么虚拟机将采用的是空闲列表法来为对象分配内存。意思是虚拟机维护了一个列表,记录上哪些内存块是可用的,再分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的内容。这种分配方式成为“空闲列表(Free List)”。
选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

1.3.4 那什么样的对象能够进入老年代(Old)

那什么样的对象能够进入老年代(Old)?
在这里插入图片描述

1.4 内存分配与回收策略

情况解析
1.对象优先在Eden分配大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC;虚拟机提供了-XX:PrintGCDetails参数,发生垃圾回收时打印内存回收日志,并且在进程退出时输出当前内存各区域的分配情况。
2.大对象直接进入老年代所谓的大对象就是指,需要大量连续内存空间的java对象,最典型的大对象就是那种很长的字符串及数组。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值得对象直接在老年代中分配(这样做的目的是避免在Eden区及两个Survivor之间发生大量的内存拷贝)
3.长期存活的对象将直接进入老年代对象年龄计数器。-XX:MaxTenuringThreshold
4、动态对象年龄判定虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等MaxTenuringThreshold中要求的年龄。
5、空间分配担保在发生Minor GC时(前),虚拟机会检测之前每次晋升到老年代的平均大小(因为当次会有多少对象会存活是无法确定的,所以取之前的平均值/经验值)是否大于老年代的剩余空间大小,如果大于,则改为直接进行一次Full GC。如果小于,则查看HandlePromotionFailure设置是否允许担保失败;如果允许,那只会进行Minor GC;如果不允许,则也要改为进行一次Full GC。取平均值进行比较其实仍然是一种动态概率手段,也就是说如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure),这样会触发Full GC。

二 垃圾回收算法分类

2.1 引用

在这里插入图片描述

2.2 GC Root的对象

在这里插入图片描述

2.3 标记-清除(Mark—Sweep)

被誉为现代垃圾回收算法的思想基础。
66

标记-清除算法采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如上图所示。标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。

2.4 复制算法(Copying)

该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。建立在存活对象少,垃圾对象多的前提下。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去后还能进行相应的内存整理,不会出现碎片问题。但缺点也是很明显,就是需要两倍内存空间。
在这里插入图片描述
 它开始时把堆分成 一个对象 面和多个空闲面, 程序从对象面为对象分配空间,当对象满了,基于copying算法的垃圾 收集就从根集中扫描活动对象,并将每个活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。一种典型的基于coping算法的垃圾回收是stop-and-copy算法,它将堆分成对象面和空闲区域面,在对象面与空闲区域面的切换过程中,程序暂停执行。

2.5 标记-整理(或标记-压缩算法,Mark-Compact,又或者叫标记清除压缩MarkSweepCompact)

此算法是结合了“标记-清除”和“复制算法”两个算法的优点。避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
在这里插入图片描述
标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。

2.6 分代回收策略(Generational Collecting)

基于这样的事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。
  新生代由于其对象存活时间短,且需要经常gc,因此采用效率较高的复制算法,其将内存区分为一个eden区和两个suvivor区,默认eden区和survivor区的比例是8:1,分配内存时先分配eden区,当eden区满时,使用复制算法进行gc,将存活对象复制到一个survivor区,当一个survivor区满时,将其存活对象复制到另一个区中,当对象存活时间大于某一阈值时,将其放入老年代。老年代和永久代因为其存活对象时间长,因此使用标记清除或标记整理算法

总结:
新生代:复制算法(新生代回收的频率很高,每次回收的耗时很短,为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表(Card Table)的数据结构,卡表为一个比特位集合,每个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对

2.7 垃圾回收器

在这里插入图片描述
垃圾回收器的任务是识别和回收垃圾对象进行内存清理,不同代可使用不同的收集器:

  • 新生代收集器使用的收集器:Serial、ParNew、Parallel Scavenge;
  • 老年代收集器使用的收集器:Serial Old(MSC)、Parallel Old、CMS。

总结:
1、Serial old和新生代的所有回收器都能搭配;也可以作为CMS回收器的备用回收器;
2、CMS只能和新生代的Serial和ParNew搭配,而且ParNew是CMS默认的新生代回收器;
3、并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
4、并发(Concurrent):指用户线程和垃圾收集线程同时执行(但不一定是并行的,可能是交替执行),用户程序继续运行,而垃圾收集程序运行在另外的CPU上。

三. GC的执行机制

Java 中的堆(deap) 也是 GC 收集垃圾的主要区域
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC(Minor GC)和Full GC(Major GC)。

  1. Scavenge GC(Minor GC): 一般情况下,当新对象生成(age=0),并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区(age+1)。然后整理(其实是复制过去就顺便整理了)Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法(即复制-清理算法),使Eden去能尽快空闲出来。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。
  2. Full GC: 对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。

3.1 触发Full GC执行的场景

在这里插入图片描述

3.2 Young GC触发条件

在这里插入图片描述

3.3 新生对象GC收回流程

基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法,(将年轻代分为3部分,主要是为了生命周期短的对象尽量留在年轻代。老年代主要存放生命周期比较长的对象,比如缓存)。可能经历过程:

  1. 对象创建时,一般在Eden区完成内存分配(有特殊);
  2. 当Eden区满了,再创建对象,会因为申请不到空间,触发minorGC,进行young(eden+1survivor)区的垃圾回收;
  3. minorGC时,Eden和survivor A不能被GC回收且年龄没有达到阈值(tenuring threshold)的对象,会被放入survivor B,始终保证一个survivor是空的;
  4. 当做第3步的时候,如果发现survivor满了,将这些对象copy到old区(分配担保机制);或者survivor并没有满,但是有些对象已经足够Old,也被放入Old区 XX:MaxTenuringThreshold;(回顾下对象进入老年代的情况)
  5. 直接清空eden和survivor A;
  6. 当Old区被放满的之后,进行fullGC。

3.4 GC日志

GC日志相关参数:

-XX:+PrintGC:输出GC日志
-XX:+PrintGCDetails:输出GC的详细日志
-XX:+PrintGCTimeStamps:输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间
-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间
-XX:+PrintHeapAtGC:在进行GC的前后打印出堆的信息
-XX:+PrintTLAB:查看TLAB空间的使用情况
-XX:PrintTenuingDistribution:查看每次minor GC后新的存活周期的阈值
-XX:PrintReferenceFC:用来跟踪系统内的(softReference)软引用,(weadReference)弱引用,(phantomReference)虚引用,显示引用过程

案例分析:-XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime一起使用

    Application time: 0.3440086 seconds
    Total time for which application threads were stopped: 0.0620105 seconds
    Application time: 0.2100691 seconds
    Total time for which application threads were stopped: 0.0890223 seconds

得知应用程序在前344毫秒中是在处理实际工作的,然后将所有线程暂停了62毫秒,紧接着又工作了210ms,然后又暂停了89ms。

2796146K->2049K(1784832K)] 4171400K->2049K(3171840K), [Metaspace: 3134K->3134K(1056768K)], 0.0571841 secs] [Times: user=0.02 sys=0.04, real=0.06 secs]
Total time for which application threads were stopped: 0.0572646 seconds, Stopping threads took: 0.0000088 seconds

应用线程被强制暂停了57ms来进行垃圾回收。其中又有8ms是用来等待所有的应用线程都到达安全点。

只要设置-XX:+PrintGCDetails 就会自动带上-verbose:gc和-XX:+PrintGC

33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]    
100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
  1. 最前面的数字“33.125:”和“100.667:”代表了GC发生的时间,这个数字的含义是从Java虚拟机启动以来经过的秒数。
  2. GC日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型,而不是用来区分新生代GC还是老年代GC的。如果有“Full”,说明这次GC是发生了Stop-The-World的。
  3. 接下来的“[DefNew”、“[Tenured”、“[Perm”表示GC发生的区域,这里显示的区域名称与使用的GC收集器是密切相关的,例如上面样例所使用的Serial收集器中的新生代名为“Default New Generation”,所以显示的是“[DefNew”。如果是ParNew收集器,新生代名称就会变为“[ParNew”,意为“Parallel New Generation”。如果采用Parallel Scavenge收集器,那它配套的新生代称为“PSYoungGen”,老年代和永久代同理,名称也是由收集器决定的。
  4. 后面方括号内部的“3324K->152K(3712K)”含义是“GC前该内存区域已使用容量-> GC后该内存区域已使用容量 (该内存区域总容量)”。而在方括号之外的“3324K->152K(11904K)”表示“GC前Java堆已使用容量 -> GC后Java堆已使用容量 (Java堆总容量)”。
  5. 再往后,“0.0025925 secs”表示该内存区域GC所占用的时间,单位是秒。有的收集器会给出更具体的时间数据
    [Full GC 283.736: [ParNew: 261599K->261599K(261952K), 0.0000288 secs]
  6. 新生代收集器ParNew的日志也会出现“[Full GC”(这一般是因为出现了分配担保失败之类的问题,所以才导致STW)。如果是调用System.gc()方法所触发的收集,那么在这里将显示“[Full GC (System)”。

3.5 减少GC开销的措施

从代码上:
在这里插入图片描述
从JVM参数上调优上:
在这里插入图片描述

3.6 内存溢出分类

在这里插入图片描述

四. 总结-JVM调优相关

4.1 调优目的

在这里插入图片描述

4.2 JVM性能调优所处的层次

在这里插入图片描述

4.3 JVM调优流程

在这里插入图片描述

4.4 性能监控工具


 调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm的调优也不例外,jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。

[文章转载:https://juejin.cn/post/6844903892774289421]

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

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

相关文章

【JavaGuide面试总结】Java IO篇

【JavaGuide面试总结】Java IO篇1.有哪些常见的 IO 模型?2.Java 中 3 种常见 IO 模型BIO (Blocking I/O)NIO (Non-blocking/New I/O)AIO (Asynchronous I/O)1.有哪些常见的 IO 模型? UNIX 系统下, IO 模型一共有 5 种: 同步阻塞 I/O、同步非阻塞 I/O、…

浏览器兼容性 问题产生原因 厂商前缀 滚动条 css hack 渐近增强 和 优雅降级 caniuse

目录浏览器兼容性问题产生原因厂商前缀滚动条css hack渐近增强 和 优雅降级caniuse浏览器兼容性 问题产生原因 市场竞争标准版本的变化 厂商前缀 比如:box-sizing, 谷歌旧版本浏览器中使用-webkit-box-sizing:border-box 市场竞争,标准没有…

Java多线程案例之线程池

前言:在讲解线程池的概念之前,我们先来谈谈线程和进程,我们知道线程诞生的目的其实是因为进程太过重量了,导致系统在 销毁/创建 进程时比较低效(具体指 内存资源的申请和释放)。 而线程,其实做…

14岁初中生将免去四考,保送清华本硕博连读,乡亲们敲锣打鼓祝贺

导语: 很多学生在很小的时候,都曾有豪言壮语:“将来一定要考上清华北大”。可是真正接受教育,开始学习之后,学生们才能发现,原来学习这么难。不要说真的走进清华北大,即使是进入“985”大学&am…

C++ 智能指针(一) auto_ptr

文章目录前言 - 什么是智能指针?std::auto_ptrauto_ptr的使用常用成员方法:1. get()方法2. release()方法3. reset()方法4. operator()5. operator*() & operator->()auto_ptr的局限性前言 - 什么是智能指针? 在全文开始之前&#xf…

Redis事务的概述、设计与实现

1 Redis事务概述事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。以下是一个事务的例子, 它…

mysql-事务以及锁原理讲解(二)

1、前言 众所周知,事务和锁是mysql中非常重要功能,同时也是面试的重点和难点。本文会详细介绍事务和锁的相关概念及其实现原理,相信大家看完之后,一定会对事务和锁有更加深入的理解。 2、什么是事务 在维基百科中,对事…

7 处理多维特征的输入

文章目录课程前提知识问题引入模型改进修改神经层的增加学习能力与超参数课本代码课程来源: 链接课程文本来源借鉴: 链接以及(强烈推荐)Birandaの课程前提知识 BCELoss - Binary CrossEntropyLoss BCELoss 是CrossEntropyLoss的一个特例&am…

JavaEE day7 初识JavaScript2

函数小结 1.可以赋值给变量(其实就是被变量所指向) 2.装入容器中作为元素存在 3.在函数调用的过程中,函数类型作为实参 4.函数作为另一个函数的返回值 可以直接return一个函数 5.和java不同,JS中允许在一个函数中定义另一个函数,也就是嵌…

介绍一个令强迫症讨厌的小红点组件

前言 在 App 的运营中,活跃度是一个重要的指标,日活/月活……为了提高活跃度,就发明了小红点,然后让强迫症用户“没法活”。 小红点虽然很讨厌,但是为了 KPI,程序员也不得不屈从运营同学的逼迫(讨好),得想办法实现。这一篇,来介绍一个徽标(Badge)组件,能够快速搞…

解决OpenEuler系统 Minimal BASH-like line editing is supported

2023年开工解决的第一个问题~呃,起因是这样的,由于业务需要,修改内核参数后重新打包内核,然后安装内核rpm包后,强制关机,结果就出现如上界面。网上搜索后绝大部分是因为安装了双系统后找不到grub系统引导文…

ELK_Elasticsearch基础介绍

目录 一、搜索是什么? 二、数据库做搜索的弊端 三、全文检索、倒排索引和Lucene 四、什么是Elasticsearch 1、Elasticsearch的功能 2、Elasticsearch的使用场景 3、Elasticsearch的特点 五、elasticsearch核心概念 一、搜索是什么? 概念&#x…

vue2与vue3面试题之区别

目录vue2与vue3面试题之区别01:数据双向绑定( proxy 替代 defineProperty)02:生命周期函数的更换03:vue3的新特性04:缓存组件与更新组件05:ref和reactive的区别06:watch和watchEffec…

测试篇(五):什么是自动化测试、自动化测试分类、selenium工具、第一个自动化测试程序

目录一、什么是自动化测试二、自动化测试分类2.1 单元测试2.2 UI自动化测试三、selenium工具3.1 selenium的介绍3.2 环境部署3.3 selenium的常用方法四、第一个自动化测试用例一、什么是自动化测试 在日常生活中我们会见到,自动化的水龙头、无人驾驶汽车、自动化的…

Mysql,使用FIND_IN_SET()函数处理多表关联问题.

这里有 user表、teacher表,其中 teacher.user_ids 字段中的值是 user.id 值以英文半角逗号拼接而来。现在, 我们需要在查询 teacher 表数据时,将 user.name 的值也查询出来。使用以下的SQL语句,即可实现需求。SELECTGROUP_CONCAT(…

系统编程中的进程的概念No.1

引言: 北京时间2023/1/28,本小编04年1月9日出生,今天第一次理解到进程的概念,所以我们接下来就学习一下什么是进程以及和进程相关的一些知识。首先我们想要了解进程以及其相关的知识,我们要先理解一下其它方面的知识&…

【2】Linux基础命令

学习笔记目录 初识Linux--入门Linux基础命令--会用Linux权限管控--懂权限Linux实用操作--熟练实战软件部署--深入掌握脚本&自动化--用的更强项目实战--学到经验云平台技术--紧跟潮流 Linux的目录结构 Linux的目录结构是一个树形结构,没有盘符这个概念&#x…

常用算法分类

按照使用场景分类排序算法,如冒泡排序,快速排序等,用于将一组数据按照特定规则排序。搜索算法,如二分查找算法,深度优先搜索算法等,用于在一组数据中查找特定元素。图论算法,如最短路径算法&…

Claude的2022年终总结——关于2022和Claude的四个问题

文章目录前言1. 我算是合格的开发者了吗2. 我算是正式的游戏人了吗3. 我算是成熟的社会人了吗4. 我算是什么样的写作者呢最后前言 2022年的这个时候,我也是在准备着年终总结,只不过应公司要求,准备述职晋升,是抱着升职加薪&#…

行为型模式 - 命令模式Command

模式的定义与特点 命令模式(Command Pattern),是将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。命令模式是把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方…