JVM的GC详解
获取GC日志方式大抵有两种
第一种就是设定JVM参数在程序启动时查看,具体的命令参数为:
-XX:+PrintGCDetails # 打印GC日志
-XX:+PrintGCTimeStamps # 打印每一次触发GC时发生的时间
第二种则是在服务器上监控:使用jstat查看,如下所示,命令格式为jstat -gc pid 输出间隔时长 输出次数
,例如笔者希望每隔1秒输出1次,并且打印5次,对应的指令如下:
jstat -gc 21608 1000 5
public static void main(String[] args) {//分配1M内存空间byte[] bytes = new byte[1024 * 1024];//触发minor gc,剩余512k,然后将1M空间存放至新生代,堆空间大约剩下1.5Mbytes = new byte[1024 * 1024];//分配至新生代约2.5Mbytes = new byte[1024 * 1024];//新生代空间不足,触发full gc,新生代空间全回收,并执行CMS GC,完成后将对象存放至新生代byte[] byte2 = new byte[2 * 1024 * 1024];}
设置JVM配置参数指明新生代、老年代堆空间大小为5M,并指明新生代Eden和survivor区的比例为8:1:1,同时我们也指定的新生代和老年代垃圾回收算法分别是ParNewGC和CMS:
-XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
- -XX:NewSize=5M:设置新生代的初始大小为 5MB。
- -XX:MaxNewSize=5M:设置新生代的最大大小为 5MB。
- -XX:InitialHeapSize=10M:设置 JVM 堆的初始大小为 10MB。
- -XX:MaxHeapSize=10M:设置 JVM 堆的最大大小为 10MB。
- -XX:SurvivorRatio=8:设置 Eden 区与 Survivor 区的比例为 8,即 Eden 占用 8/10 的新生代空间,两个 Survivor 各占 1/10。
- -XX:PretenureSizeThreshold=10M:设置对象直接进入老年代的阈值为 10MB,超过这个大小的对象会直接分配到老年代。
- -XX:+UseParNewGC:启用并行新生成收集器(Parallel New Generation Collector),用于多线程环境下的新生代垃圾回收。
- -XX:+UseConcMarkSweepGC:启用并发标记清除收集器(Concurrent Mark Sweep Collector),用于多线程环境下的老年代垃圾回收。
- -XX:+PrintGCDetails:打印详细的垃圾回收日志信息。
- -XX:+PrintGCTimeStamps:在垃圾回收日志中添加时间戳。
GC过程:
-
首先代码执行到byte[] bytes = new byte[1024 * 1024];,此时新生代空间充裕,没有任何输出。
-
执行第二行代码bytes = new byte[1024 * 1024];再次进程内存分配时,发现新生代空间不足出现以此minor gc,对应输出结果如下,
2.938: [GC (Allocation Failure) 2.938: [ParNew: 3348K->512K(4608K), 0.0016244 secs] 3348K->1692K(9728K), 0.0016904 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
我们大体可以看出GC原因是Allocation Failure即新生代不能分配对象,触发一次新生代GC,新生代GC前后空间由3348K变为512K,整堆空间由3348K变为1692K,最后输出了GC耗时、系统响应耗时以及应用程序暂停时间
完成上述GC,将1M的数组存放至新生代,此时新生代的堆空间大约是1M:
-
然后第三行再次分配数组,新生代空间充裕,直接存入:
-
最后一次分配2M数组时,新生代空间不足且空间分配担保失败,直接触发FULL GC,从日志中我们可以看到minor gc直接将上述的所有字节数组都回收了:
9.689: [GC (Allocation Failure) 9.689: [ParNew: 2626K->0K(4608K), 0.0021520 secs] 3806K->2746K(9728K), 0.0021903 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-
最后就是CMS老年代GC,首先进行初始标记阶段该阶段为STW并找到所有的GC root,从日志中我们看到老年代使用的容量为2718K且总容量为5120K,后面的4766K(9728K)标记为当前堆的实际大小和总容量:
2.057: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2718K(5120K)] 4766K(9728K), 0.0005690 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-
然后进入并发标记阶段该阶段不会STW,先是CMS-concurrent-mark标记gc root可达对象,然后CMS-concurrent-preclean重新并发扫描进入到老年代的对象,最后时CMS-concurrent-abortable-preclean该阶段并发运行至eden区空间占用率达到满意:
2.058: [CMS-concurrent-mark-start] 2.059: [CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2.059: [CMS-concurrent-preclean-start] 2.059: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2.059: [CMS-concurrent-abortable-preclean-start]CMS: abort preclean due to time 7.163: [CMS-concurrent-abortable-preclean: 0.005/5.105 secs] [Times: user=0.00 sys=0.00, real=5.10 secs]
-
最后就到了最终标记阶段,该阶段会STW,从日志输出可以看出新生代占用2048k,当前这个重新标记阶段Rescan 花费了0.0004620 secs,其余就是处理弱引用、卸载无用的类以及清理元数据等花费时间和耗时:
7.164: [GC (CMS Final Remark) [YG occupancy: 2048 K (4608 K)]7.164: [Rescan (parallel) , 0.0004620 secs]7.164: [weak refs processing, 0.0001727 secs]7.164: [class unloading, 0.0005772 secs]7.165: [scrub symbol table, 0.0011975 secs]7.166: [scrub string table, 0.0003404 secs][1 CMS-remark: 2718K(5120K)] 4766K(9728K), 0.0030256 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-
最后就是并发的清理垃圾会重置标记,等待下一个周期的GC:
7.167: [CMS-concurrent-sweep-start] 7.168: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 7.168: [CMS-concurrent-reset-start] 7.168: [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-
最后我们查看内存使用情况可以看到,新生代的2M就是我们最后分配的数组,在eden区,而老年代使用了1677K:
Heappar new generation total 4608K, used 2089K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)eden space 4096K, 51% used [0x00000000ff600000, 0x00000000ff80a558, 0x00000000ffa00000)from space 512K, 0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)to space 512K, 0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)concurrent mark-sweep generation total 5120K, used 1677K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)Metaspace used 3124K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 327K, capacity 386K, committed 512K, reserved 1048576K
频繁的gc
- Minor GC:发生在年轻代的空间回收,包含eden和survivor,也叫做Young GC。
- Major GC:在老年代堆区进行空间回收。
- Full GC:清理所有堆区的内存空间的垃圾内存,包括年轻代和老年代。
频繁的 minor gc 和major gc
public static void main(String[] args) throws Exception {while (true) {//分配3M数组byte[] bytes = new byte[1024 * 1024];bytes = new byte[1024 * 1024];bytes = new byte[1024 * 1024];//创建2M的新对象触发GCbyte[] byte2 = new byte[2 * 1024 * 1024];Thread.sleep(1000);}}
设置该程序的堆内存新生代为5M,按照8:1:1的比例分配,这也就意为着Eden区内存大小为4M,然后S区分别是512K,这也就意味着在待分配对象加Eden区堆空间超过4M就会触发minor gc:
为了演示年轻代的回收行为,我们需要在对这个应用程序的年轻代堆内存改为5M,且Eden区和S区的比例为8:1:1,同时也打印GC日志信息:
-XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
输出结果如下,GC日志显示每秒基本都会触发一次Minor GC,进而间接导致频繁的major gc:
结合配置可知,我们频繁分配对象导致新生代进行频繁的GC,又因为S区大小无法容纳存活的对象,进而使得这些对象提前进入老年代,导致major GC也随之频繁,所以 解决 的办法也比较简单,按照等比例调整大堆空间,即将新生代堆空间调整至10M,保证S区各有2M空间以容纳新生代存活的对象:
-XX:NewSize=10M -XX:MaxNewSize=10M -XX:InitialHeapSize=100M -XX:MaxHeapSize=100M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
可以看到经过调整之后,基本上Minor gc就能解决问题:
将年轻代空间调大,是否会更加耗时?
答案是不会的,原因如以:
- JVM操作本质上都是内存操作,相对而言不会太慢。
- 将一次GC的时间拆分为t1和t2,t1是扫描年轻代空间是否有垃圾的时间,这个时间的几乎可以忽略不计。而t2则是将eden空间存活的对象复制到survivor区的时间,这个复制操作则是t1时间的10倍。
- 由此可以看出,避免耗时的正确做法是合理评估新生代堆空间,减少非必要的复制操作,所以说调整新生代的空间并不会导致进一步的耗时问题。
频繁的FULL GC
模拟一个场景,我们的应用中有一个定时任务,这个定时任务每隔1s会想另一个定时任务线程池中提交100个任务,每个任务都会针对Obj 对象进行方法调用:
@Component
public class Task {private static Logger logger = LoggerFactory.getLogger(Task.class);private static final ScheduledThreadPoolExecutor executor =new ScheduledThreadPoolExecutor(50,new ThreadPoolExecutor.DiscardOldestPolicy());private static class Obj {private String name = "name";private int age = 18;private String gender = "man";private LocalDate birthday = LocalDate.MAX;public void func() {//这个方法什么也不做}//返回count个Obj对象private static List<Obj> getObjList(int count) {List<Obj> objList = new ArrayList<>(count);for (int i = 0; i != count; ++i) {objList.add(new Obj());}return objList;}}@Scheduled(cron = "0/1 * * * * ? ") //每1秒执行一次public void execute() {logger.info("1s一次定时任务");//向线程池提交100个任务Obj.getObjList(100).forEach(i -> executor.scheduleWithFixedDelay(i::func, 2, 3, TimeUnit.SECONDS));}
}
完成后我们设置下面这段JVM参数后,将其启动:
-Xms20M -Xmx20M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
不久后,控制台出现频繁的full gc,如果在生产环境,频繁的full gc导致stw会导致系统吞吐量下降:
1288.133: [Full GC (Allocation Failure) 1288.133: [CMS1288.142: [CMS-concurrent-preclean: 0.012/0.012 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] (concurrent mode failure): 13695K->13695K(13696K), 0.0610050 secs] 19839K->19836K(19840K), [Metaspace: 29026K->29026K(1077248K)], 0.0610521 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
1288.258: [Full GC (Allocation Failure) 1288.258: [CMS: 13695K->13695K(13696K), 0.0612134 secs] 19839K->19836K(19840K), [Metaspace: 29026K->29026K(1077248K)], 0.0612676 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
1288.320: [GC (CMS Initial Mark) [1 CMS-initial-mark: 13695K(13696K)] 19836K(19840K), 0.0041303 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
排查思路
定位到程序号后,使用jstat -gc pid10000 10
观察其gc情况,可以看到每隔10s,就会增加大量的full gc:
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
640.0 640.0 0.0 640.0 5504.0 665.5 13696.0 11176.2 31488.0 28992.6 4352.0 3889.5 39 0.084 15 0.100 0.184
640.0 640.0 0.0 640.0 5504.0 1487.2 13696.0 11176.2 31488.0 28992.6 4352.0 3889.5 39 0.084 25 0.142 0.227
640.0 640.0 0.0 640.0 5504.0 1697.8 13696.0 11176.2 31488.0 28992.6 4352.0 3889.5 39 0.084 35 0.185 0.269
再查看jmap -heap pid
查看堆区使用情况,可以看到老年代的使用率还是蛮高的:
Attaching to process ID 26176, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.212-b10using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GCHeap Configuration:MinHeapFreeRatio = 40MaxHeapFreeRatio = 70MaxHeapSize = 20971520 (20.0MB)NewSize = 6946816 (6.625MB)MaxNewSize = 6946816 (6.625MB)OldSize = 14024704 (13.375MB)NewRatio = 2SurvivorRatio = 8MetaspaceSize = 21807104 (20.796875MB)CompressedClassSpaceSize = 1073741824 (1024.0MB)MaxMetaspaceSize = 17592186044415 MBG1HeapRegionSize = 0 (0.0MB)Heap Usage:
New Generation (Eden + 1 Survivor Space):capacity = 6291456 (6.0MB)used = 5088288 (4.852569580078125MB)free = 1203168 (1.147430419921875MB)80.87615966796875% used
Eden Space:capacity = 5636096 (5.375MB)used = 5088288 (4.852569580078125MB)free = 547808 (0.522430419921875MB)90.28036428052326% used
From Space:capacity = 655360 (0.625MB)used = 0 (0.0MB)free = 655360 (0.625MB)0.0% used
To Space:capacity = 655360 (0.625MB)used = 0 (0.0MB)free = 655360 (0.625MB)0.0% used
concurrent mark-sweep generation:capacity = 14024704 (13.375MB)used = 13819664 (13.179458618164062MB)free = 205040 (0.1955413818359375MB)98.53800836010514% used12064 interned Strings occupying 1120288 bytes.
在排除内存泄漏的问题后,我们通过jmap定位进程中导致是什么对象导致老年代堆区被大量占用:
jmap -histo 7476 | head -n 20
可以看到前20名中的对象都是和定时任务相关,有一个Task$Obj对象非常抢眼,很明显就是因为它的数量过多导致的,此时我们就可以通过定位代码确定如何解决,常见方案无非是: 优化代码、增加空间两种方式,一般来说我们都会采用代码优化的方式去解决。
num #instances #bytes class name
----------------------------------------------1: 50760 3654720 java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask2: 30799 2901552 [C3: 88986 2847552 java.util.concurrent.locks.AbstractQueuedSynchronizer$Node4: 50700 1622400 com.example.jstackTest.Task$Obj5: 50760 1218240 java.util.concurrent.Executors$RunnableAdapter6: 50700 811200 com.example.jstackTest.Task$$Lambda$587/16055533137: 6391 707928 java.lang.Class8: 29256 702144 java.lang.String9: 13577 434464 java.util.concurrent.ConcurrentHashMap$Node10: 6363 341016 [Ljava.lang.Object;11: 1722 312440 [B12: 3414 230424 [I13: 4 210680 [Ljava.util.concurrent.RunnableScheduledFuture;14: 5223 208920 java.util.LinkedHashMap$Entry15: 2297 202136 java.lang.reflect.Method16: 2262 193760 [Ljava.util.HashMap$Node;17: 5668 181376 java.util.HashMap$Node
而本次问题也很明显,任务是一个个提交到定时任务线程池中,是由于定时任务队列DelayedWorkQueue不断堆积任务导致内存被打满。所以最终改成将一个批处理一次性提交到定时任务中立刻将这一批对象回收从而避免耗时任务堆积一堆对象:
@Scheduled(cron = "0/1 * * * * ? ") //每1秒执行一次public void execute() {logger.info("1s一次定时任务");//向线程池提交100个任务executor.scheduleWithFixedDelay(() -> {Obj.getObjList(100).forEach(i -> i.func());}, 2, 3, TimeUnit.SECONDS);}
频繁FULL GC的原因和解决对策
总的来说原因可以频繁FULL GC分为3个:
- 用户频繁调用System.gc():这种情况需要修改代码即可,我们不该频繁调用这个方法的。
- 老年区空间过小:视情况适当扩大空间。
- 大对象过多:这种情况视情况决定是扩大老年代空间或者将大对象拆分。
一般来说,我们优先考虑调整堆内存空间,其次才是针对业务逻辑的代码处理进行更进一步的优化。
相关文章:
JVM的GC详解
获取GC日志方式大抵有两种 第一种就是设定JVM参数在程序启动时查看,具体的命令参数为: -XX:PrintGCDetails # 打印GC日志 -XX:PrintGCTimeStamps # 打印每一次触发GC时发生的时间第二种则是在服务器上监控:使用jstat查看,如下所示,命令格式为jstat -gc…...
反转单向链表以及单链表添加节点、遍历单链表
反转1个单向链表 /*** 节点类*/ class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val val;}Overridepublic String toString() {return "ListNode{" "val" val ", next" next };} }借助一个pre来存储每…...
ZZNUOJ(C/C++)基础练习1021——1030(详解版)
目录 1021 : 三数求大值 C语言版 C版 代码逻辑解释 1022 : 三整数排序 C语言版 C版 代码逻辑解释 补充 (C语言版,三目运算)C类似 代码逻辑解释 1023 : 大小写转换 C语言版 C版 1024 : 计算字母序号 C语言版 C版 代码逻辑总结…...
Linux学习笔记——系统维护命令
一、进程管理 1、ps命令(查) 来自process缩写,显示当前的进程状态。包括:进程的号码,发起者,系统资源,使用占比,运行状态等等。 语法格式:ps 参数 实例&#x…...
Harbor 部署
harbor镜像仓库搭建 版本v2.10.3 文章目录 一. docker 安装 harbor1. harbor 配置http访问1.1 下载harbor二进制包1.2 修改配置文件1.3 运行1.4 访问 2.【可选】harbor 配置https访问2.1 自签证书2.1 修改配置文件2.3 修改hosts文件2.4 运行2.5 访问 二. k8s 安装harbor1 .安装…...
three.js+WebGL踩坑经验合集(6.1):负缩放,负定矩阵和行列式的关系(2D版本)
春节忙完一轮,总算可以继续来写博客了。希望在春节假期结束之前能多更新几篇。 这一篇会偏理论多一点。笔者本没打算在这一系列里面重点讲理论,所以像相机矩阵推导这种网上已经很多优质文章的内容,笔者就一笔带过。 然而关于负缩放…...
开源的瓷砖式图像板系统Pinry
简介 什么是 Pinry ? Pinry 是一个开源的瓷砖式图像板系统,旨在帮助用户轻松保存、标记和分享图像、视频和网页。它提供了一种便于快速浏览的格式,适合喜欢整理和分享多种媒体内容的人。 主要特点 图像抓取和在线预览:支持从网页…...
LabVIEW透镜多参数自动检测系统
在现代制造业中,提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统,该系统能够在单一工位上完成透镜的多项质量参数检测,并实现透镜的自动搬运与分选,极大地提升了检…...
socket实现HTTP请求,参考HttpURLConnection源码解析
背景 有台服务器,网卡绑定有2个ip地址,分别为: A:192.168.111.201 B:192.168.111.202 在这台服务器请求目标地址 C:192.168.111.203 时必须使用B作为源地址才能访问目标地址C,在这台服务器默认…...
反向代理模块jmh
1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求,然后将请求转发给内部网络上的服务器,将从服务器上得到的结果返回给客户端,此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说,反向代理就相当…...
安卓(android)实现注册界面【Android移动开发基础案例教程(第2版)黑马程序员】
一、实验目的(如果代码有错漏,可查看源码) 1.掌握LinearLayout、RelativeLayout、FrameLayout等布局的综合使用。 2.掌握ImageView、TextView、EditText、CheckBox、Button、RadioGroup、RadioButton、ListView、RecyclerView等控件在项目中的…...
RubyFPV开源代码之系统简介
RubyFPV开源代码之系统简介 1. 源由2. 工程架构3. 特性介绍(软件)3.1 特性亮点3.2 数字优势3.3 使用功能 4. DEMO推荐(硬件)4.1 天空端4.2 地面端4.3 按键硬件Raspberry PiRadxa 3W/E/C 5. 软件设计6. 参考资料 1. 源由 RubyFPV以…...
解锁维特比算法:探寻复杂系统的最优解密码
引言 在复杂的技术世界中,维特比算法以其独特的魅力和广泛的应用,成为通信、自然语言处理、生物信息学等领域的关键技术。今天,让我们一同深入探索维特比算法的奥秘。 一、维特比算法的诞生背景 维特比算法由安德鲁・维特比在 1967 年提出…...
Unity游戏(Assault空对地打击)开发(2) 基础场景布置
目录 导入插件 文件夹整理 场景布置 山地场景 导入插件 打开【My Assets】(如果你刚进行上篇的操作,该窗口默认已经打开了)。 找到添加的几个插件,点击Download并Import x.x to...。 文件夹整理 我们的目录下多了两个文件夹&a…...
Office / WPS 公式、Mathtype 公式输入花体字、空心字
注:引文主要看注意事项。 1、Office / WPS 公式中字体转换 花体字 字体选择 “Eulid Math One” 空心字 字体选择 “Eulid Math Two” 使用空心字时,一般不用斜体,取消勾选 “斜体”。 2、Mathtype 公式输入花体字、空心字 2.1 直接输…...
代码随想录算法训练营第三十九天-动态规划-213. 打家劫舍 II
与上一题基本一样,只不过房间形成一个环,就需要在首尾考虑状况多一些这不是多一些状况的问题,是完全不知道如何选择的问题这种状况详细分析一下就是要分成三种情况 第一种:不考虑首元素,也不考虑尾元素,只考…...
自然语言处理-词嵌入 (Word Embeddings)
词嵌入(Word Embedding)是一种将单词或短语映射到高维向量空间的技术,使其能够以数学方式表示单词之间的关系。词嵌入能够捕捉语义信息,使得相似的词在向量空间中具有相近的表示。 📌 常见词嵌入方法 基于矩阵分解的方…...
Redis 数据备份与恢复
Redis 数据备份与恢复 引言 Redis 是一款高性能的键值对存储系统,广泛应用于缓存、消息队列、分布式锁等领域。为了保证数据的安全性和可靠性,定期对 Redis 数据进行备份与恢复是至关重要的。本文将详细介绍 Redis 数据备份与恢复的方法,帮助您更好地管理和维护 Redis 数据…...
【leetcode】T541 (两点反思)
解题反思 闷着头往,往往会写成一团浆糊,还推倒重来,谋划好全局思路再开始很重要。 熟悉C的工具库很重要,一开始看到反转就还想着用stack来着,后面突然想起来用reverse函数刚好可以用哇,这题也就迎刃而解了…...
《STL基础之vector、list、deque》
【vector、list、deque导读】vector、list、deque这三种序列式的容器,算是比较的基础容器,也是大家在日常开发中常用到的容器,因为底层用到的数据结构比较简单,笔者就将他们三者放到一起做下对比分析,介绍下基本用法&a…...
嵌入式系统|DMA和SPI
文章目录 DMA(直接内存访问)DMA底层原理1. 关键组件2. 工作机制3. DMA传输模式 SPI(串行外设接口)SPI的基本原理SPI连接示例 DMA与SPI的共同作用 DMA(直接内存访问) 类型:DMA是一种数据传输接口…...
LevelDB 源码阅读:写入键值的工程实现和优化细节
读、写键值是 KV 数据库中最重要的两个操作,LevelDB 中提供了一个 Put 接口,用于写入键值对。使用方法很简单: leveldb::Status status leveldb::DB::Open(options, "./db", &db); status db->Put(leveldb::WriteOptions…...
寒假刷题Day18
一、16. 最接近的三数之和 这一题有负数,没有单调性,不能“大了右指针左移,小了左指针右移,最后存值域求差绝对值”。 class Solution { public:int threeSumClosest(vector<int>& nums, int target) {ranges::sort(n…...
力扣219.存在重复元素每日一题(大年初一)
以一道简单题开启全新的一年 哈希表:我们可以使用 哈希表 来存储数组元素及其对应的索引。通过遍历数组,我们可以检查当前元素是否已经存在于哈希表中,并判断索引差是否满足条件。 具体步骤如下: 创建一个哈希表 map,…...
Midjourney中的强变化、弱变化、局部重绘的本质区别以及其有多逆天的功能
开篇 Midjourney中有3个图片“微调”,它们分别为: 强变化;弱变化;局部重绘; 在Discord里分别都是用命令唤出的,但如今随着AI技术的发达在类似AI可人一类的纯图形化界面中,我们发觉这样的逆天…...
Blazor-选择循环语句
今天我们来说说Blazor选择语句和循环语句。 下面我们以一个简单的例子来讲解相关的语法,我已经创建好了一个Student类,以此类来进行语法的运用 因为我们需要交互性所以我们将类创建在*.client目录下 if 我们做一个学生信息的显示,Gender为…...
根据每月流量和市场份额排名前20 的AI工具列表
ChatGPT:由Open AI研发,是一款对话式大型语言模型。它能够理解自然语言输入,生成连贯且符合逻辑的回复。可用于文本创作,如撰写文章、故事、诗歌;还能解答各种领域的知识问题,提供翻译、代码解释等服务&…...
关于安卓greendao打包时报错问题修复
背景 项目在使用greendao的时候,debug安装没有问题,一到打包签名就报了。 环境 win10 jdk17 gradle8 项目依赖情况 博主的greendao是一个独立的module项目,项目目前只适配了java,不支持Kotlin。然后被外部集成。greendao版本…...
前端面试笔试题目(一)
以下模拟了大厂前端面试流程,并给出了涵盖HTML、CSS、JavaScript等基础和进阶知识的前端笔试题目,以帮助你更好地准备面试。 面试流程模拟 1. 自我介绍(5 - 10分钟):面试官会请你进行简单的自我介绍,包括…...
网络工程师 (10)设备管理
前言 设备管理中的数据传输控制方式是确保设备与内存(或CPU)之间高效、准确地进行数据传送的关键。 一、程序直接控制方式 1.工作原理: 由CPU发出I/O指令,直接控制数据的传输过程。CPU需要不断查询外设的状态,以确定数…...
如何让一个用户具备创建审批流程的权限
最近碰到一个问题,两个sandbox,照理用户的权限应该是一样的,结果开发环境里面我可以左右的做各种管理工作,但是使用change set上传后,另一个环境的同一个用户,没有相对于的权限,权限不足。 当时…...
unity学习23:场景scene相关,场景信息,场景跳转
目录 1 默认场景和Assets里的场景 1.1 scene的作用 1.2 scene作为project的入口 1.3 默认场景 2 场景scene相关 2.1 创建scene 2.2 切换场景 2.3 build中的场景,在构建中包含的场景 (否则会认为是失效的Scene) 2.4 Scenes in Bui…...
【Java高并发】基于任务类型创建不同的线程池
文章目录 一. 按照任务类型对线程池进行分类1. IO密集型任务的线程数2. CPU密集型任务的线程数3. 混合型任务的线程数 二. 线程数越多越好吗三. Redis 单线程的高效性 使用线程池的好处主要有以下三点: 降低资源消耗:线程是稀缺资源,如果无限…...
全网首发,MacMiniA1347安装飞牛最新系统0.8.36,改造双盘位NAS,超详细.36,改造双盘位nas,超详细
全网首发,MacMiniA1347安装飞牛最新系统0.8.36,改造双盘位NAS,超详细 小伙伴们大家好呀,勤奋的凯尔森同学又双叒叕来啦,今天这一期也是有点特别,我们把MacMiniA1347安装飞牛最新系统0.8.36,并且…...
简要介绍C++中的 max 和 min 函数以及返回值
简要介绍C中的 max 和 min 函数 在C中,std::max 和 std::min 是标准库 <algorithm> 中提供的函数,用于比较两个或多个值并返回最大值或最小值。这些函数非常强大且灵活,支持多种数据类型(如整数、浮点数、字符串等ÿ…...
【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户注册
🧸安清h:个人主页 🎥个人专栏:【计算机网络】【Mybatis篇】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 目录 🎯项目基本介绍 🚦项…...
记忆化搜索(5题)
是什么? 是一个带备忘录的递归 如何实现记忆化搜索 1.添加一个备忘录(建立一个可变参数和返回值的映射关系) 2.递归每次返回的时候把结果放到备忘录里 3.在每次进入递归的时候往备忘录里面看看。 目录 1.斐波那契数列 2.不同路径 3.最…...
强化学习笔记——4策略迭代、值迭代、TD算法
基于策略迭代的贝尔曼方程和基于值迭代的贝尔曼方程,关系还是不太理解 首先梳理一下: 通过贝尔曼方程将强化学习转化为值迭代和策略迭代两种问题 求解上述两种贝尔曼方程有三种方法:DP(有模型),MCÿ…...
nginx目录结构和配置文件
nginx目录结构 [rootlocalhost ~]# tree /usr/local/nginx /usr/local/nginx ├── client_body_temp # POST 大文件暂存目录 ├── conf # Nginx所有配置文件的目录 │ ├── fastcgi.conf # fastcgi相关参…...
Spring RESTful API 设计与实现
Spring RESTful API的设计与实现极大地提升了开发效率和系统可维护性,通过遵循RESTful设计原则,使得API结构清晰、行为一致,便于扩展和维护。它在构建微服务架构中扮演着核心角色,支持松耦合的通信,同时通过标准的HTTP协议和数据格式增强了系统的互操作性。结合Spring Sec…...
【玩转全栈】--创建一个自己的vue项目
目录 vue介绍 创建vue项目 vue页面介绍 element-plus组件库 启动项目 vue介绍 Vue.js 是一款轻量级、易于上手的前端 JavaScript 框架,旨在简化用户界面的开发。它采用了响应式数据绑定和组件化的设计理念,使得开发者可以通过声明式的方式轻松管理数据和…...
【Envi遥感图像处理】008:波段(批量)分离与波段合成
文章目录 一、波段分离提取1. 提取单个波段2. 批量提取单个波段二、波段合成相关阅读:【ArcGIS微课1000例】0058:波段合成(CompositeBands)工具的使用 一、波段分离提取 1. 提取单个波段...
数据结构-Stack和栈
1.栈 1.1什么是栈 栈是一种特殊的线性表,只允许在固定的一段进行插入和删除操作,进行插入和删除操作的一段称为栈顶,另一端称为栈底。 栈中的数据元素遵顼后进先出LIFO(Last In First Out)的原则,就像一…...
内容检索(2025.01.30)
随着创作数量的增加,博客文章所涉及的内容越来越庞杂,为了更为方便地阅读,后续更新发布的文章将陆续在此汇总并附上原文链接,感兴趣的小伙伴们可持续关注文章发布动态! 博客域名:http://my-signal.blog.cs…...
牛客周赛 Round 77
题目目录 C-小红走网格解题思路参考代码 D-隐匿社交网络解题思路参考代码 F-计树解题思路参考代码 C-小红走网格 解题思路 根据裴蜀定理:设a,b是不全为0的整数,对任意整数x,y,满足gcd(a,b&…...
c++面试:类定义为什么可以放到头文件中
这个问题是刚了解预编译的时候产生的疑惑。 声明是指向编译器告知某个变量、函数或类的存在及其类型,但并不分配实际的存储空间。声明的主要目的是让编译器知道如何解析程序中的符号引用。定义不仅告诉编译器实体的存在,还会为该实体分配存储空间&#…...
Oracle查看数据库表空间使用情况
Oracle RAC环境查看表空间使用情况 查询字段释义: NEED_ADDFILE,--是否需增加表空间文件 TABLESPACE_NAME,--表空间名称 TABLESPACE_FILE_COUNT, --表空间当前数据文件数量 NOW_FILEENABLE_BLOCKS,--表空间文件当前数据块数 NOW_FILEENABLE_BYTES_GB,--表空间文件当…...
Spring Boot 热部署实现指南
在开发 Spring Bot 项目时,热部署功能能够显著提升开发效率,让开发者无需频繁重启服务器就能看到代码修改后的效果。下面为大家详细介绍一种实现 Spring Boot 热部署的方法,同时也欢迎大家补充其他实现形式。 步骤一、开启 IDEA 自动编译功能…...
如何构建ObjC语言编译环境?构建无比简洁的clang编译ObjC环境?Windows搭建Swift语言编译环境?
如何构建ObjC语言编译环境? 除了在线ObjC编译器,本地环境Windows/Mac/Linux均可以搭建ObjC编译环境。 Mac自然不用多说,ObjC是亲儿子。(WSL Ubuntu 22.04) Ubuntu可以安装gobjc/gnustep和gnustep-devel构建编译环境。 sudo apt-get install gobjc gnus…...
C++——类和对象(下)
1.初始化列表 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个…...