Java虚拟机中的垃圾回收
2 垃圾回收
2.1 判断一个对象是否可回收
2.1.1 引用计数法
如果一个对象被另一个对象引用,那么它的引用计数加一,如果那个对象不再引用它了,那么引用计数减一。当引用计数为 0 时,该对象就应该被垃圾回收了。
但是下面这种互相引用的情况就无法回收了:
两个对象的计数都为1,导致两个对象都无法被释放
2.1.2 可达性分析算法
垃圾回收之前,扫描所有对象,判断每个对象是否被根对象引用,如果没有被根对象引用,那么在以后垃圾回收时就将那些没有与根相连的对象回收
- Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以回收
- 哪些对象可以作为 GC Root ?
查找可以作为GCRoot的对象:
运行下面程序:
public static void main(String[] args) throws IOException {ArrayList<Object> list = new ArrayList<>();list.add("a");list.add("b");list.add(1);System.out.println(1);System.in.read();list = null;System.out.println(2);System.in.read();System.out.println("end");}
使用 jmap -dump:format=b,live,file=1.bin 进程id
将堆内存中的信息存储到文件1.bin中
使用Eclipse Memory Analyzer 打开1.bin文件,选择 GC Root 选项进行分析:
可以看出 GC Root 分为四类:
- System Class :系统类,启动类加载器加载的类,例如Object,HashMap等核心类
- Native Stack:Java 有时候需要调用系统中的一些方法
- Busy Monitor :被加锁的对象也需要被保留
- Thread:活动线程的栈帧内中使用的对象
2.1.3 五种引用
-
强引用
只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
例如:C对象和B对象强引用了 A1 对象,只有 C 对象和 B 对象都不强引用了 A1 对象,A1 对象才可以被垃圾回收
- 软引用(SoftReference)
- 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象
- 可以配合引用队列来释放软引用自身
- 弱引用(WeakReference)
- 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
- 可以配合引用队列来释放弱引用自身
- 虚引用(PhantomReference)
- 必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存
Cleaner 对象虚引用 ByteBuffer 对象
当ByteBuffer 对象被回收后,其分配的直接内存还没有被回收。
这时Cleaner对象会被放入引用队列:
虚引用所在的队列会由一个ReferenceHandler的线程来定时的在队列中寻找是否有新入队的 Cleaner ,如果有就会调用 Cleaner 对象中的 clean 方法。而 clean 方法就会根据前面记录的直接内存的地址,用 Unsafe.freeMemory 方法释放直接内存:
- 终结器引用(FinalReference)
- 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象
暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象
所有对象都继承自Object类,其中有一个 finalize 方法
如果 finalize 方法被重写了,也就是被对象实现了。jvm 就会将终结器引用对象放入引用队列。就会有一个FinalizeHandler的线程对队列进行检查。找到了终结器引用,就会根据它找到该对象,调用 finalize 方法。调用完后,第二次垃圾回收才可以把该对象回收掉
2.1.4 软引用的示例
内存不足时,会回收软引用对象,可用于对象缓存
可以用 SoftReference 对象保证要软引用的对象:
首先限制内存大小的 vm option :-Xmx20m
public class d1_SoftReference {public static void main(String[] args) {// list -> SoftReference -> byte[]List<SoftReference<byte[]>> list = new ArrayList<>();for (int i = 0; i < 5; i++) {SoftReference<byte[]> reference = new SoftReference<>(new byte[1024 * 1024 * 4]);System.out.println(reference.get());list.add(reference);System.out.println(list.size());}System.out.println("循环结束" + list.size());for (SoftReference<byte[]> reference : list) {System.out.println(reference.get());}}
}
输出:可以看出由于内存空间不足,前三个ref都被回收掉了
[B@10f87f48
1
[B@b4c966a
2
[B@2f4d3709
3
[B@4e50df2e
4
[B@1d81eb93
5
循环结束5
null
null
null
[B@4e50df2e
[B@1d81eb93
软引用,引用的对象为 null,那么软引用本身也就没有必要保留了。这里可以用软引用队列来对软引用对象本身进行回收
public class d1_SoftReference {public static void main(String[] args) {List<SoftReference<byte[]>> list = new ArrayList<>();// 引用队列ReferenceQueue<byte[]> queue = new ReferenceQueue<>();for (int i = 0; i < 5; i++) {// 这里关联了软引用队列,当 byte[]被回收时,软引用本身会被加入到 queue中SoftReference<byte[]> reference = new SoftReference<>(new byte[1024 * 1024 * 4], queue);System.out.println(reference.get());list.add(reference);System.out.println(list.size());}// 清除掉没有内容的软引用本身:Reference<? extends byte[]> poll = queue.poll();while (poll != null){list.remove(poll);poll = queue.poll();}System.out.println("循环结束" + list.size());for (SoftReference<byte[]> reference : list) {System.out.println(reference.get());}}
}
输出:这里的list中只有2个byte数组了
[B@10f87f48
1
[B@b4c966a
2
[B@2f4d3709
3
[B@4e50df2e
4
[B@1d81eb93
5
循环结束2
[B@4e50df2e
[B@1d81eb93
2.1.5 弱引用示例
不管内存是否充足,如果只有弱引用引用该对象,就会回收该对象
// -Xmx20m
public class d2_WeakReference {public static void main(String[] args) {// list -> WeakReference -> byte[]List<WeakReference<byte[]>> list = new ArrayList<>();for (int i = 0; i < 5; i++) {WeakReference<byte[]> ref = new WeakReference<>(new byte[1024*1024*4]);list.add(ref);for (WeakReference<byte[]> weakReference : list) {System.out.println(weakReference.get() + " ");}System.out.println();}System.out.println("循环结束:" + list.size());}
}
输出:在垃圾回收时会将弱引用对象回收:
[B@10f87f48 [B@10f87f48
[B@1d81eb93 [B@10f87f48
[B@1d81eb93
[B@7291c18f null
null
null
[B@34a245ab null
null
null
null
null 循环结束:5
2.2 垃圾回收算法
2.2.1 标记清除
Mark Sweep
记录垃圾对象的地址
优点:速度快
缺点:造成内存碎片,本可以存下的新对象因为内存分布的太分散而无法存下
2.2.2 标记整理
将所有存活的对象移动到一端,避免了内存碎片的产生,但是由于对象发生了移动,所以算法的速度慢
- 速度慢
- 没内存碎片
2.2.3 复制
把内存空间分为两部分。一部分内存空间用完了,就把存活的对象复制到另一部分的内存空间上面。然后把使用过的内存空间进行清理。
- 不会产生碎片
- 占用双倍的内存空间
2.3 分代垃圾回收
- 对象首先分配在伊甸园区
- 当新生代空间不足时,触发 minor gc,伊甸园和 from 区域存货的对象复制到 to 区域,存活的对象年龄加 1 ,并交换 from 和 to 两个区域
- minor gc 会引发 stop the world ,暂停其他用户线程,等待垃圾回收结束,用户线程才恢复运行
- 对象年龄的寿命超过阈值 15 时,会晋升到老年代
- 当老年代空间不足时,会首先触发 minor gc,如果之后空间仍然不足,就会触发 full gc,STW 的时间更长
对于大对象,新生代无法存入,就会直接存入老年代
如果大对象老年代都无法存下,就会抛出内存溢出的异常
多线程下运行,一个线程的内存溢出不会影响其他线程中断
2.3.1 相关 VM 参数
-Xms
:堆初始大小
-Xmx
或 -XX:MaxHeapSize=size
:堆最大大小
-Xmn
或 -XX:NewSize=size + -XX:MaxNewSize=size
新生代大小
-XX:InitialSurvivorRatio=ratio
和 -XX:+UseAdaptiveSizePolicy
:幸存区比例(动态)
-XX:SurvivorRatio=ratio
幸存区比例
-XX:MaxTenuringThreshold=threshold
晋升阈值
-XX:+PrintTenuringDistribution
晋升详情
-XX:+PrintGCDetails -verbose:gc
GC详情
-XX:+ScavengeBeforeFullGC
FullGC 前 MinorGC
2.4 垃圾回收器
-
串行
- 单线程的垃圾回收期,回收时,其他线程暂停
- 适合个人电脑,其堆内存较小,CPU个数少的
- 吞吐量优先
-
多线程运行
- 堆内存较大,多核 CPU 支持(如果不是多核,就需要争抢时间片)
- 让单位时间内,STW 的时间最短
- 垃圾回收时间占比少:0.2 0.2 = 0.4
-
响应时间优先
- 多线程运行
- 堆内存较大,多核 CPU 支持
- 尽可能让单次的 STW 的时间最短
- 每次0.1,回收多次:0.1 0.1 0.1 0.1 0.1 = 0.5
2.4.1 串行
VM 参数:-XX:+UseSerialGC=Serial+SerialOld
Serial : 工作在新生代,回收算法是复制算法
SerialOld:工作在老年代,回收算法是标记整理算法
所有线程在安全点前阻塞,单线程的垃圾回收器运行;因为可能会更改对象地址,所以线程需要阻塞(STW)
2.4.2 吞吐量优先
-XX:+UseParallelGC
:新生代采用复制算法
-XX:+UseParallelOldGC
:老年代采用标记整理了算法
Paralle
表示回收器是并行的
垃圾回收器的线程数默认是和CPU的核数相同的。
-XX:+UseAdaptiveSizePolicy
:自动调整垃圾回收的参数,例如eden,from,to,晋升阈值的大小
-XX:GCTimeRation=ratio
: 1 / ( 1 + r a t i o ) 1/(1+ratio) 1/(1+ratio) ,ratio 默认99,垃圾回收的时间占比为1%;但是难以达到,一般设置19;调整垃圾回收时间与总时间的占比,用于调整吞吐量
-XX:MaxGCPauseMillis=ms
:默认为 200 ms;用于调整每次垃圾回收的暂停时间
2.4.3 响应时间优先 CMS
-XX:+UseConcMarkSweepGC
:并发标记清除回收器,用于老年代;
-XX:+UseParNewGC
:新生代的复制算法
如果并发出现问题,例如标记清除算法会产生很多内存碎片就会并发失败,老年代的垃圾回收器就会退化成 SerialOld
的复制串行回收器
老年代空间不足,达到运行点,进行初始标记,对那些根对象进行遍历
随后进行并发标记遍历其他对象,与此同时其他线程继续运行
当进行重新标记时,需要STW, 随后进行重新标记
ParallelGCThreads=n
-XX:ConcGCThreads=threads
:可以设置并发标记线程数量;清理垃圾的同时,可能会产生新垃圾,得等到下次垃圾回收时才能释放,称为浮动垃圾。所以需要为这些浮动垃圾预留空间。
-XX:CMSInitiatingOccupancyFraction=percent
:执行垃圾回收时的内存占比
-XX:+CMSScavengeBeforeRemark
:重新标记时,可能新生代的对象引用老年代的对象,就需要扫描整个堆进行可达性分析,而这些新生代的对象将来也是要被回收掉的,就导致做了无用的查找工作。这个参数可以在重新标记前,对新生代进行一次垃圾回收,这样新生代的对象就少了,查找的工作就少了
2.4.4 G1
Garbage First 简称 G1 收集器。
2017 JDK 9默认
适用场景:
- 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms
- 超大堆内存,会将堆划分为多个大小相等的 Region
- 整体上是标记+整理算法,两个区域之间是复制算法
相关 JVM 参数:
-XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time
1)垃圾回收阶段
2)Young Collection
新生代内存紧张后,就会将新生代的Eden区域的垃圾回收对象复制到幸存区 survivor
当 survivor 区域的年龄超过晋升阈值就会复制到老年区 Old
3) Young Collection +CM(concurrent Mark,并发标记)
-
在Young GC时会进行 GC Root的初始标记
-
老年代占用堆空间达到阈值时,进行并发标记(不会STW),由下面的 VM 参数决定
-XX:InitiatingHeapOccupancyPercent=percent
(默认45%)
4)混合收集
对 E,S,O 进行全面垃圾回收
- 最后标记 (Remark) 会 STW。因为是并发的,其他线程可能产生新的垃圾,所以需要进行最后标记
- 拷贝存活 (Evacuation) 会 STW
Eden 中的对象会复制到 Survivor ,Surivior 区域年龄达到晋升阈值会复制到 Old
Old 区域会根据设置的垃圾回收时间有选择的回收部分老年代的对象,挑选那些回收价值高的也就是能释放内存更多的对象进行回收。
垃圾回收暂停时间设置:-XX:MaxGCPauseMillis=ms
5)Full GC
- SerialGC
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc - ParallelGC
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc - CMS
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足 - G1
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足
6) Young Collection 跨代引用
新生代回收的跨代引用(老年代引用新生代)问题
新生代回收时需要查找有哪些 GC Root,再进行可达性分析和回收
查找 GC Root 需要查找整个老年代花费时间多。
可以把老年代区域分割成多个 card,如果这个 card 引用了新生代,那么这个 card 就是脏 card。
- 老年代里有卡表,新生代里有 Remembered Set 记录有那些脏 card 引用自己
- 垃圾回收时就会通过 Remembered Set 找到脏card里的GC Root
- 在引用变更时通过 post-write barrier + dirty card queue
- concurrent refinement threads 更新 Remembered Set
7) Remark
pre-write barrier + satb_mark_queue
C的引用发生改变则会添加写屏障,并放入队列,将来的Remark阶段就会通过队列对C对象进行处理决定是否垃圾回收
8)JDK 8u20 字符串去重
-
优点:节省大量内存
-
缺点:略微多占用了 cpu 时间,新生代回收时间略微增加
开启这个开关(默认打开):
-XX:+UseStringDeduplication
String s1 = new String("hello"); // char[]{'h','e','l','l','o'}
String s2 = new String("hello"); // char[]{'h','e','l','l','o'}
- 将所有新分配的字符串放入一个队列
- 当新生代回收时,G1并发检查是否有字符串重复
- 如果它们值一样,让它们引用同一个 char[]
- 注意,与 String.intern() 不一样
- String.intern() 关注的是字符串对象
- 而字符串去重关注的是 char[]
- 在 JVM 内部,使用了不同的字符串表
9) JDK 8u40 并发标记类卸载
所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类
-XX:+ClassUnloadingWithConcurrentMark
默认启用
10)JDK 8u60 回收巨型对象
- 一个对象大于 region 的一半时,称之为巨型对象
- G1 不会对巨型对象进行拷贝
- 回收时被优先考虑
- G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0 的巨型对象就可以在新生代垃圾回收时处理掉
11)JDK 9 并发标记起始时间的调整
- 并发标记必须在堆空间占满前完成,否则退化为 FullGC
- JDK 9 之前需要使用
-XX:InitiatingHeapOccupancyPercent
- JDK 9 可以动态调整
-XX:InitiatingHeapOccupancyPercent
用来设置初始值- 进行数据采样并动态调整
- 总会添加一个安全的空档空间
2.5 垃圾回收调优
2.5.1 调优领域
- 内存
- 锁竞争
- CPU 占用
- IO 占用
2.5.2 确定目标
-
低延迟还是高吞吐量,选择合适的回收器
-
响应时间优先:CMS,G1,ZGC
-
吞吐量优先:ParallelGC
2.5.3 最快的 GC 是不 GC
- 查看 FullGC 前后的内存占用,考虑下面的问题:
- 数据是不是太多?
- resultSet = statement.executeQuery(“select *from large_table”) 查出的数据太多,可以加个
limit n
限制一下查出数据的数量
- resultSet = statement.executeQuery(“select *from large_table”) 查出的数据太多,可以加个
- 数据表示是否太臃肿?
- 对象图,只查出只用的数据,例如查找年龄,不要也把姓名其他信息查出来
- 对象大小设置,一个Object 最小占16字节,包装类型占24字节,基本类型int只占4字节
- 是否存在内存泄漏?
- 创建一个static Map map = xxx,长时间存货对象
- 可以用软引用,弱引用,进行回收
- 使用第三方缓存实现
- 数据是不是太多?
2.5.4 新生代调优
新生代特点:
- 所有的 new 操作的内存分配非常廉价
- 首先会分配在TLAB中(thread-local allocation buffer)
- 死亡对象的回收代价是零
- 大部分对象用过即死
- Minor GC 的时间远远低于 Full GC
新生代内存是否越大越好?
-Xmn
:设置新生代内存的vm命令
Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery). GC is performed in this region more often than in other regions. If the size for the young generation is too small, then a lot of minor garbage collections are performed. If the size is too large, then only full garbage collections are performed, which can take a long time to complete. Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.
-
新生代能容纳所有【并发量 * (请求-响应)】的数据
-
幸存区大到能保留【当前活跃对象+需要晋升对象】
-
晋升阈值配置得当,让长时间存活对象尽快晋升
-XX:MaxTenuringThreshold=threshold
-XX:+PrintTenuringDistribution
2.5.5 老年代调优
以 CMS 为例,具有浮动垃圾的问题,如果浮动垃圾存不下就会并发失败退化为Serial
- CMS 的老年代内存越大越好
- 先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代
- 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
-XX:CMSInitiatingOccupancyFraction=percent
2.5.6 案例
- 案例1 Full GC 和 Minor GC频繁
- 新生代内存太小,创建的对象生成周期短,但因新生代内存下而晋升到老年代
- 可以调大新生代内存,同时调大晋升阈值
- 案例2 请求高峰期发生 Full GC,单次暂停时间特别长 (CMS)
- 重新标记时会扫描所有对象,耗时太多
- 设置
-XX:+CMSScavengeBeforeRemark
:重新标记前回收一遍新生代的垃圾
- 案例3 老年代充裕情况下,发生 Full GC (CMS jdk1.7)
- 1.7 永久代内存空间不足也会导致Full GC
- 1.7 方法区存放在永久代,可以增大方法区的内存
相关文章:
Java虚拟机中的垃圾回收
2 垃圾回收 2.1 判断一个对象是否可回收 2.1.1 引用计数法 如果一个对象被另一个对象引用,那么它的引用计数加一,如果那个对象不再引用它了,那么引用计数减一。当引用计数为 0 时,该对象就应该被垃圾回收了。 但是下面这种互相…...
Vscode新手安装与使用
安装与版本选择 VS Code 有两个不同的发布渠道:一个是我们经常使用的稳定版(Stable),每个月发布一个主版本;另外一个发布渠道叫做 Insiders,每周一到周五 UTC 时间早上6点从最新的代码发布一个版本&#x…...
以元旦为题的诗词(二)
都放假了吧,都有空了吧,可坐下来好好学学诗词,好好写些诗词了吧,我先来几首,你实在不行,去百度或者小程序搜索《美诗计》写一写 元旦 去年元日落寒灰,今岁清明在此杯 老眼看书如梦寐ÿ…...
饥荒Mod 开发(二一):超大便携背包,超大物品栏,永久保鲜
饥荒Mod 开发(二十):显示打怪伤害值 饥荒Mod 开发(二二):显示物品信息 源码 游戏中的物品栏容量实在太小了,虽然可以放在箱子里面但是真的很不方便,外出一趟不容易看到东西都不能捡。实在是虐心。 游戏中的食物还有变质机制,时间长了就不能吃了,玩这个游戏心里压力真是太…...
js 七种继承方法
目录 1. 第一种方法:原型链继承 2. 第二种方法:构造函数继承(call继承) 3. 第三种方法:组合式继承 4. 第四种方法:拷贝继承 5. 第五种方法:原型式继承 6. 第六种方法...
Unity Shader 实现X光效果
Unity Shader 实现X光效果 Unity Shader 实现实物遮挡外轮廓发光效果第五人格黎明杀机火炬之光 实现方案操作实现立体感优化总结源码 Unity Shader 实现实物遮挡外轮廓发光效果 之前看过《火炬之光》、《黎明杀机》、《第五人格》等不少的游戏里面人物被建筑物遮挡呈现出不同的…...
Android Camera相关类功能整理
1.Camera Java相关类 代码目录:frameworks/base/core/java/android/hardware/camera2/ CameraManager:camera系统服务管理类,用于监测、获取特征值和连接Camera设备。 CameraManager.CameraManagerGlobal:全局camera Manager实例ÿ…...
3、Git分支操作与团队协作
Git分支操作 1.什么是分支2. 分支的好处3. 分支的操作3.1 查看分支3.2 创建分支3.3 切换分支3.4 修改分支3.5 合并分支3.6 产生和解决冲突 4. 创建分支和切换分支图解5. Git团队协作机制团队内协作跨团队协作 均在git bash中进行操作。事先建好本地工作库 1.什么是分支 在版本…...
Linux网卡配置
一、网卡配置 1、目录参数 /etc/syscofig/network-scripts/ifcfg-e*** /etc 目录时Linux系统的配置文件,有相对权限的用户能修改目录,但普通用户都可以访问。 sysconfig /etc/sysyconfig目录包含了Linux的系统配置文件 network-scripts network-scripts…...
wireshark access/trunk/hybrid报文分析
1,access接口 发送带vlan的报文 wireshark交换机配置 [Huawei-GigabitEthernet0/0/1] [Huawei-GigabitEthernet0/0/1]port link-type access [Huawei-GigabitEthernet0/0/1]port default vlan 100 [Huawei-GigabitEthernet0/0/2]port link-type access [Huawei-Gig…...
C++ Primer Plus----第十二章--类和动态内存分布
本章内容包括:对类成员使用动态内存分配;隐式和显式复制构造函数;隐式和显式重载赋值运算符;在构造函数中使用new所必须完成的工作;使用静态类成员;将定位new运算符用于对象;使用指向对象的指针…...
清理 Oracle Arch 目录中的日志文件
清理Oracle Arch目录中的文件需要遵循以下步骤: 1、登陆Oracle数据库服务器:确保你有权限访问Oracle数据库服务器,并且能够登录到服务器操作系统。 ssh 192.168.1.200 su - oracle2、启动RMAN(Recovery Manager)&…...
PicGo+GitHub搭建免费图床
PicGoGitHub搭建免费图床 步骤 1: 安装 PicGo步骤 2:创建图床仓库步骤 3: 配置 GitHub Token步骤 4: 配置 PicGo步骤 5: 上传图片步骤 6: 访问图片 使用 GitHub 作为图床的优势在于免费、稳定且具有版本控制功能,特别适合个人博客、小型项目等。PicGo作为一个开源的…...
免费在线客服软件推荐:经济实用的客户沟通解决方案
好用的在线客服软件是企业是必不可少的工具,他让企业流程更流畅高效,让客户服务更完善优质。市场上的在线客服软件有很多,说着免费使用的软件也不在少数。今天小编就来推荐一款免费在线客服软件。 不过,我们选择免费在线客服软件…...
leetcode344. 反转字符串
题目描述 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1: 输入:s ["h&quo…...
【js自定义鼠标样式】【js自定义鼠标动画】
文章目录 前言一、效果图二、实现步骤1. 去除原有鼠标样式2. 自定义鼠标样式3. 使用 总结 前言 自定义鼠标形状,自定义鼠标的动画,可以让我们的页面更加有设计感。 当前需求:吧鼠标自定义成一个正方形,鼠标的效果有:和…...
Linux开发工具——gdb篇
Linux下调试工具——gdb 文章目录 makefile自动化构建工具 gdb背景 gdb的使用 常用命令 总结 前言: 编写代码我们使用vim,编译代码我们使用gcc/g,但是我们,不能保证代码没问题,所以调试是必不可少的。与gcc/vim一样&…...
spring状态机
1、概述 Spring State Machine 是一个用于处理状态机逻辑的框架,它提供了一种简洁的方法来定义状 态、转换以及在状态变更时触发的动作。 概念 状态 ( State ) :一个状态机至少要包含两个状态。例如自动门的例子,有 …...
K8S异常处理
一、概述 1、k8s有时候会报错The connection to the server ip:6443 was refused - did you specify the right host or port ,本文档提供几种可能产生该报错的原因和排障思路。 二、发现问题 使用任意Kubectl 命令会报错:The connection to the serv…...
【挑战全网最易懂】深度强化学习 --- 零基础指南
深度强化学习介绍、概念 强化学习介绍离散场景,使用行为价值方法连续场景,使用概率分布方法实时反馈连续场景:使用概率分布 行为价值方法 强化学习六要素设计奖励函数设计评论家策略学习与优化 算法路径深度 Q 网络 DQN演员-评论家算法&…...
WPF RelativeSource
RelativeSource 类在 WPF 中提供了以下几种模式: RelativeSource Self:指定当前元素作为相对源。可以在当前元素的属性中绑定到自身的属性。 示例: <TextBlock Text"{Binding Text, RelativeSource{RelativeSource Self}}" /&…...
centos 安装 配置 zsh
centos 编译安装 zsh 和 配置 oh-my-zsh 下载 wget https://jaist.dl.sourceforge.net/project/zsh/zsh/5.9/zsh-5.9.tar.xz依赖 yum install ncurses-devel安装zsh 执行: tar -xvf zsh-5.9.tar.xz cd zsh-5.9 ./configure --prefix/usr/local/zsh5.9 make &am…...
git 常用基本命令, reset 回退撤销commit,解决gitignore无效,忽略记录或未记录远程仓库的文件,删除远程仓库文件
git 基本命令 reset 撤销commit https://blog.csdn.net/a704397849/article/details/135220091 idea 中 rest 撤销commit过程如下: Git -> Rest Head… 在To Commit中的HEAD后面加上^,点击Reset即可撤回最近一次的尚未push的commit Reset Type 有三…...
Vue Echarts 多折线图只有一条X轴(合并X轴数据并去重排序) - 附完整示例
echarts:一个基于 JavaScript 的开源可视化图表库。 目录 效果 一、介绍 1、官方文档:Apache ECharts 2、官方示例 二、准备工作 1、安装依赖包 2、示例版本 三、使用步骤 1、在单页面引入 echarts 2、指定容器并设置容器宽高 3、数据处理&am…...
WPF+Halcon 培训项目实战(6):目标匹配助手
文章目录 前言相关链接项目专栏模板匹配助手简单使用金字塔级别参数自动选择应用插入代码 总结 前言 为了更好地去学习WPFHalcon,我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享,想要源码…...
Linux管理LVM逻辑卷
目录 一、LVM逻辑卷介绍 1. 概述 2. LVM基本术语 2.1 PV(Physical Volume,物理卷) 2.2 VG (Volume Group,卷组) 2.3 LV (Logical Volume,逻辑卷) 3. 常用的磁盘命令 4. 查看系统信息的命…...
vue如何实现局部刷新?
应用场景: 比如你要切换tap栏实现刷新下面form表单等,相当于刷新页面。 如何使用如下: <div v-if"isReloadData"> 比如你想刷新那个位置就把 v-if"isReloadData"写到那个标签上 </div> 在data中定义刷新标…...
C语言,指针链表详解解说及代码示例
C语言,指针链表详解解说及代码示例 指针链表是一种常用的数据结构,用于存储和组织数据。它由一系列节点组成,每个节点包含数据和一个指向下一个节点的指针。通过这种方式,可以动态地添加、删除和访问节点,实现灵活的数…...
6、LLaVA
简介 LLaVA官网 LLaVA使用Vicuna(LLaMA-2)作为LLM f ϕ ( ⋅ ) f_\phi() fϕ(⋅),使用预训练的CLIP图像编码器 ViT-L/14 g ( X v ) g(X_v) g(Xv)。 输入图像 X v X_v Xv,首先获取feature Z v g ( X v ) Z_vg(X_v) Zvg(Xv)。考虑到最后一…...
SpringMVC核心处理流程梳理
1、处理流程图展示 当我拿出这张图,阁下又该如何应对呢?执行流程是不是一目了然了。【记住一句话:所有的注解都只是一个标签或者标记,最终都是反射找到具体方法上面的注解标记,然后找到类、属性、方法扩展自己想要的功…...
公司网站建设需求书/亚马逊排名seo
在JMeter BeanShell 前置处理器中,您可以使用下面的代码来定义长度为2的数组: int[] myArray new int[2]; myArray[0] 1; myArray[1] 2;该代码创建了一个名为"myArray"的整型数组,并初始化为具有两个元素,分别为1和2。…...
网站上papi酱做的音频/搜狗站长平台打不开
Web基本笔记~12.引用数据类型 上一期 引用类型的值(对象)是引用类型的一个实例。在JavaScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当。尽管JavaScript从技术上讲…...
昆明网站建设推荐q479185700上墙/百度推广客服电话多少
SwiftUI 问答之 在初始化之前使用的变量 @State var 初始化问题 实战问题 我收到以下编译错误:“在初始化之前使用了变量‘self.entryData’”如何修复它并在我的 init 方法中正确初始化 @state var entryData? struct EditEntryView: View {@Environment(\.managedObject…...
网站建设制作免费推广/网络怎么推广自己的产品
JVM第六卷---类加载机制类加载机制加载链接验证准备解析初始化----< cinit >()V 方法发生的时机练习类加载器启动类加载器扩展类加载器双亲委派模式线程上下文类加载器自定义类加载器破坏双亲委派模型的几种做法类加载机制 Java虚拟机把描述类结构的数据从Class文件中加…...
三屏合一网站建设/百度广告怎么收费标准
见字如面,我是军哥!话说这几天又来了一大波读者,有朋友说加我微信提示繁忙,所以必须给解决一下撒!我的朋友圈会发一些公号之外的感悟,比如读者问题问卷调查、趣味抽奖,说实话还挺好玩的…...
无锡新吴区住房和建设交通局网站/电脑系统优化软件哪个好用
首先我们尝试构造一个乱码 第1步 我们可以在首页编写一个提交的表单,在web目录下创建form.jsp <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>Title</title> </head…...