JVM/GC复习1---更新中

JVM(java虚拟机)

1.在生产环境中出现应用卡主的现象,日志不输出,程序没有反应
2.cpu占用过高
3.在多线程应用下,如何分配线程的数量
4.死锁
5.内存泄漏
等等

参数类型 实例 解释说明
标准参数 -help 很稳定,占时的JVM版本中都不会进行改变
-X参数(非标准参数) -Xint 不稳定,在未来的一些版本中可能会进行一些改变
-XX参数(使用率较高) -XX:UseSerialGC 再JVM的调优或者JVM的debugger中使用

//-showversion 参数表示先打印版本信息,再去执行后面的命令,再调试的时候非常有用
情况\参数 -server -clinet
初始堆空间 大一些 小一些
默认回收器 并行垃圾回收器 串行垃圾回收器
启动速度
运行速度
32位操作系统(window) - 默认
32位操作系统(其他)2GB以上的内存,2个以上的CPU 默认 否则选择该模式
64位操作系统 不支持
// 进入根目录创建文件
mkdir /test
cd /test/
ll
vim TestJVM.java
//将刚刚的代码复制进去
    public static void main(String[] args) {
   
        String str = System.getProperty("str");
        if (str == null) {
   
            System.out.println("this str =null");
        } else {
   
            System.out.println(str);
        }
    }
//然后编译
javac TestJVM.java
//运行文件
java TestJVM
//设置里面的参数进行运行
java -Dstr=hello TestJVM
//结果如下

在这里插入图片描述

//按照刚刚的方式使用showversion,打印出来的就是显示版本号的基本进信息然后就是this str =null的信息
java -client -showversion TestJVM

java -server-showversion TestJVM

java -X

-X参数 作用 解释说明
-Xmixed 混合模式执行 (默认) 价格解释模式和编译迷失进行混合使用,由JVM自己决定,这是jvm的默认模式也是推荐模式
-Xint 仅解释模式执行 标记会强制JVM执行所有的字节代码,这样就会降低了运行速度,通常是低10倍以上
-Xcomp 编译模式 和Xint相反,jvm再第一次使用的时候会把所有的字节码编译到本地代码,从而大大的速度上的优化,但是很多应用在使用这个模式的时候都会有一些性能的损失.但是比使用-Xint损失少点,原因是他没有让JIT启动it编译器的全部功能,JIT编译器可以Udine是否需要编译做出判断,如果所有的代码都进行了编译,对于一些只执行一次代码就没有意义了
-Xbootclasspath <用 ; 分隔的目录和 zip/jar 文件>设置搜索路径以引导类和资源
-Xbootclasspath/a <用 ; 分隔的目录和 zip/jar 文件> 附加在引导类路径末尾
-Xbootclasspath/p <用 ; 分隔的目录和 zip/jar 文件>置于引导类路径之前
-Xdiag 显示附加诊断消息
-Xnoclassgc 禁用类垃圾收集
-Xincgc 启用增量垃圾收集
-Xloggc:< file > 将 GC 状态记录在文件中 (带时间戳)
-Xbatch 禁用后台编译
-Xms< size > 设置初始 Java 堆大小
-Xmx< size > 设置最大 Java 堆大小
-Xss< size > 设置 Java 线程堆栈大小
-Xprof 输出 cpu 配置文件数据
-Xfuture 启用最严格的检查, 预期将来的默认值
-Xrs 减少 Java/VM 对操作系统信号的使用 (请参阅文档)
-Xcheck:jni 对 JNI 函数执行其他检查
-Xshare:off 不尝试使用共享类数据
-Xshare:auto 在可能的情况下使用共享类数据 (默认)
-Xshare:on 要求使用共享类数据, 否则将失败。
-XshowSettings 显示所有设置并继续
-XshowSettings:all 显示所有设置并继续
-XshowSettings:vm 显示所有与 vm 相关的设置并继续
-XshowSettings:properties 显示所有属性设置并继续
-XshowSettings:locale 显示所有与区域设置相关的设置并继续

-XX参数

类型 格式 实例
boolean类型 -XX:[+ -]< name >表示启用或者禁用< name >属性 -XX:DisableExplicitGC表示禁用手动调用GC操作,也就是说System.gc()无效
非boolean类型 -XX:< name > =< value >表示< name >属性值为< value > -XX:NewRatio = 1表示新生代和老年代的比值

下面的属于-XX的参数范围

参数 解释说明
-Xms 设置JVM的堆内存初始大小
-Xmx 设置JVM的堆内存最大大小
//比如运行下面的程序实例
java -Xms512m -Xmx2048m TestJVM

什么时候需要查看JVM的运行参数

1.运行java命令的时候打印出运行参数
运行java命令时候打印参数需要添加-XX:PrintFlagsFinal参数即可
实例:java -XX:PrintFlagsFinal TestJVM
在这里插入图片描述上面图的等于说明是默认值,如果显示的是:=说明是被修改之后的值
如果现在我想要将ZerroTLAB修改为true只需要这样就可以了
java -XX:PrintFlagsFinal -XX:+ZerroTLAB TestJVM

2.查看正在运行的java进程的参数
如果需要查看正在运行的jvm就需要借助月jinfo命令来进行查看
首先,启动一个tomcat用于测试来观察运行的jvm参数
cd /tmp/
rz 上传
tar -xvf apache-tomcat-7.0.57.tar.gz
cd apache-tomcat-7.0.57
cd bin/
./startup.sh
访问路径为:http://localhost:8080/


使用jps查看进程
使用jps -l 查看进程的全包名
查看所有的参数,用法:jinfo -flags < 进程id >
jinfo -flags 5269
在这里插入图片描述

jvm的内存模型
1.7版本
在这里插入图片描述
1.8版本
在这里插入图片描述在这里插入图片描述
可以看出1.8版本的组成为:年轻代 + 老年代
年轻代: Eden + 2个Survivor
老年代:OldGen
元数据空间:所占用的内存空间不是再虚拟机内部的,而是再本地内存空间中
在这里插入图片描述通过jstat命令进行查看堆内存的使用情况

jstat命令可以查看堆内存各个部分的使用量,以及加载类的数量格式如下:
jstat [-命令选项] [vmid][间隔时间/毫秒][查询次数]
查看class加载统计
jps
7080 jps
14440
jstat -class 14440
在这里插入图片描述

参数 解释说明
Loaded 加载class的数量
Bytes 所占用的空间大小
Unloaded 未加载数量
Bytes 未加载占用的空间
Time 总时间

查看编译统计
jstat -compilr 14440
在这里插入图片描述

参数 解释说明
Compiled 编译的数量
Failed 失败数量
Invalid 不可用数量
Time 总时间
FailedType 失败类型
FailedMethod 失败的方法

垃圾回收统计
jstat -gc 14440
在这里插入图片描述
这里指的是每一秒打印一次一共打印5次
jstat -gc 14440 1000 5

参数 解释说明
S0C 第一个Survivor区的大小
S1C 第二个Survivor区的大小
S0U 第一个Survivor区的使用大小
S1U 第二个Survivor区的使用大小
EU Eden区的大小
EC Eden区的使用大小
OC Old区的大小
OU Old区的使用大小
MC 方法区大小
MU 方法区使用大小
CCSC 压缩类空间大小
CCSU 压缩类空间使用大小
YGC 年轻代垃圾回收次数
YGCT 年轻代垃圾回收耗时时间
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收耗时时间
GCT 垃圾回收耗时总时长

jmap的使用
查看内存的使用情况
jmap -heap 27472
在这里插入图片描述
查看内存中对象的数量以及大小
查看所有的对象,包括活跃的和非活跃的对象
jmap -histo < pid > | more
在这里插入图片描述

查看活跃的对象
jmap -histo:live < pid > | more
例如:jmap -histo:live: 27472 | more
在这里插入图片描述

对象 说明
B BYTE
C CHAR
D DOUBLE
F FLOAT
I INT
J LONG
Z BOOLEAN
[ 数组例如:[I表示的就是int[]
[L+类名 其他对象

将内存使用的情况dump到文件中
有时候需要将JVM当前的内存情况dump(快照)到文件中,然后针对这个文件进行分析,jmap的用法
jmap -dump:format=b,file=dumpFileName < pid >
例如:jmap -dump:format=b,file=H:\ dump.dat 27472
再使用jvm的导入到MAT工具中进行分析需要用到的参数是–当我们发生内存溢出的时候,将我们的内存使用情况进行一个快照(设置内存的初始内存大小,设置内存的最大内存大小)
-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
在这里插入图片描述在这里插入图片描述生成的文件名就是上面图片提到的java_pid7600.hprof文件
在这里插入图片描述

使用MAT工具对dump文件进行分析
网址:https://www.eclipse.org/mat/

在这里插入图片描述打开文件
在这里插入图片描述
在这里插入图片描述选择之前的dump的文件
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

GC

垃圾回收:程序运行的时候必然需要申请内存的资源,无效的对象资源如果不及时进行处理的就会一直占用内存资源,最终导致内存溢出
java语言中有自动的垃圾回收机制就是GC

垃圾回收算法

1.引用计数法

原理:假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败的时候,对象A的引用计数器就-1,如果对象A的计数器值为0,就是说明A没有被引用,可以被回收了


优点:
1.实用性较高,无需等到内存不够的时候,才可以进行回收,运行的时候根据对象的计数器是否为0,就可以直接进行回收了
2.在垃圾回收的过程中,引用无需挂起,如果申请内存的时候内存不足,则会立即outofmenber错误
3.区域性,更新对象的计数器的时候,只是影响到该对象,不会扫描全部的对象


缺点:
1.对象每次被引用的时候,都需要去更新计数器,有一定时间的开销
2.浪费cpu资源,即使是内存足够的情况下,任然运行时进行着计数器的统计
3.无法解决循环引用的问题(最大的缺点)

循环引用
A a = new A();
B b = new B();
a.b=b;
b.a=a;
a=null;
b=null;

2.标记清除发

是将垃圾护手分为2个阶段,分别为标记和清除
1.标记:从根节点开始标记引用的对象
2.清除:未被标记引用的对象就是垃圾回收对象,可以被清理
暂停程序线程,没有被标记的对象会被回收清除掉然后被标记的对象留下来并进行重置变为未标记的状态,恢复程序线程,程序继续运行


优点:
1.解决了循环引用的问题


缺点:
1.效率比较低,标记和清除2个动作都是需要遍历所有的对象,并且再GC的时候需要暂停应用程序,对于交互性要求高的应用而言这个体验是非常差的
2.通过标记清除的算法清理出来的内存,碎片化比较严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来内存是不连贯的

3.标记压缩算法

再标记清除算法的基础之上做的优化,和标记清除算法一样,也是从根节点开始,对对象的应用进行标记,在清理的阶段,并不是简单地清理未标记的对象,而是将存货的对象压缩到哦内存的一段,然后清理边界意外的垃圾,从而解决碎片化的问题
在这里插入图片描述


优点:解决了标记清除法里面而定碎片化问题
缺点:标记压缩算法多了一个压缩的步骤,这样就会导致其中的清除的整体效率受到了影响

4.复制算法

复制算法的核心是:将原有的内存空间一分为二,每次只用到其中的一块,在垃圾回收的时候,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换内存的角色,完成垃圾回收
如果内存的垃圾对象比较多的情况下需要复制的对象比较少,这种情况下是和使用这个方式并且效率比较高反之不适用
在这里插入图片描述


优点:
1.在垃圾对象多的情况下,效率高
2.清理以后,内存无碎片


缺点:
1.再垃圾对象少的时候不适合
2.分配的2块内存空间,再同一时刻只能使用一半,内存使用率较低

5.分代算法

分代算法指的是根据回收对象的特点进行选择,再jvm中,年轻代适合复制算法,老年代适合标记清除或者标记压缩算法

算法 优点 缺点 说明
引用计数法 1.实用性较高,无需等到内存不够的时候,才可以进行回收,运行的时候根据对象的计数器是否为0,就可以直接进行回收了 2.在垃圾回收的过程中,引用无需挂起,如果申请内存的时候内存不足,则会立即outofmenber错误 3.区域性,更新对象的计数器的时候,只是影响到该对象,不会扫描全部的对象 1.对象每次被引用的时候,都需要去更新计数器,有一定时间的开销2.浪费cpu资源,即使是内存足够的情况下,任然运行时进行着计数器的统计 3.无法解决循环引用的问题(最大的缺点) 假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败的时候,对象A的引用计数器就-1,如果对象A的计数器值为0,就是说明A没有被引用,可以被回收了
标记清除算法 1.解决了循环引用的问题 1.效率比较低,标记和清除2个动作都是需要遍历所有的对象,并且再GC的时候需要暂停应用程序,对于交互性要求高的应用而言这个体验是非常差的 2.通过标记清除的算法清理出来的内存,碎片化比较严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来内存是不连贯的 是将垃圾护手分为2个阶段,分别为标记和清除1.标记:从根节点开始标记引用的对象2.清除:未被标记引用的对象就是垃圾回收对象,可以被清理;暂停程序线程,没有被标记的对象会被回收清除掉然后被标记的对象留下来并进行重置变为未标记的状态,恢复程序线程,程序继续运行
标记压缩算法 解决了标记清除法里面而定碎片化问题 标记压缩算法多了一个压缩的步骤,这样就会导致其中的清除的整体效率受到了影响 再标记清除算法的基础之上做的优化,和标记清除算法一样,也是从根节点开始,对对象的应用进行标记,在清理的阶段,并不是简单地清理未标记的对象,而是将存货的对象压缩到哦内存的一段,然后清理边界意外的垃圾,从而解决碎片化的问题
复制算法 1.在垃圾对象多的情况下,效率高 2.清理以后,内存无碎片 1.再垃圾对象少的时候不适合 2.分配的2块内存空间,再同一时刻只能使用一半,内存使用率较低 将原有的内存空间一分为二,每次只用到其中的一块,在垃圾回收的时候,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换内存的角色,完成垃圾回收
分代算法 分代算法指的是根据回收对象的特点进行选择,再jvm中,年轻代适合复制算法,老年代适合标记清除或者标记压缩算法

收集器

参数 说明
-XX:UseSerialGC 指定的年轻代和老年代都使用串行垃圾收集器
-XX:+PrintGCDetails 打印垃圾回收的详细信息
-XX:UseParallelGC 年轻代使用ParallelGC垃圾回收期,老年代使用串行回收器
-XX:UseParallelOldGC 年轻代使用ParallelGC垃圾回收期,老年代使用ParallelOld垃圾回收器
-XX:MaxGCPauseMillis (设置最大的垃圾收集时候的停顿时间,单位毫秒,需要注意的是ParallelGC为了达到设置的停顿时间,可能会调整堆的大小或者其他的参数,如果堆的大小设置的比较小,就会导致GC工作变得很频繁,反而可能会影响到性能,这个参数使用的时候需要谨慎处理)
-XX:GCTimeRatio (设置垃圾回收时间占程序运行时间的百分比,公式:1/(1+n),他的值为0~100之间的数字,默认值为99,也就是垃圾回收事假不能超过1%)
-XX:UseAdaptiveSizePolicy (自适应GC模式,垃圾回收器将会自动的调整新生代,老年代等参数,达到吞吐量,堆大小,停顿时间之间的平衡;一般用于手动的调整比较困难的场景,让收集器自动的进行调整)
-XX:+UseG1GC 使用G1垃圾收集器
-XX:+MaxGCPauseMillis 设置期望达到的最大GC停顿时间指标,默认是200毫秒,但是jvm不一定能保证达到这个指标
-XX:+ParallelGCthreads=n 设置STW工作线程数的值,将n的值设置为逻辑处理器的值.n的值与逻辑处理器的数量相同,最多为8
-XX:+ConcGCThreads=n 设置并行标记的线程数,将n设置为并行垃圾回收线程数的1/4左右
-XX:+G1HeapRegionSize 设置的G1区域的大小,值是2的幂,范围是1mb~32mb之间,目标是根据最小dejava堆大小划分为≈2048个区域, 默认是堆内存的1/2000
-XX:+InitiatingHeapOccupancyPercent=n 设置处罚标记周期的java堆占用率阀值,默认占用率是整个java堆的45%

在这里插入图片描述

参数 解读 冒号后面的内容解释
DefNew 表示使用的是串行垃圾收集器 4416K->512K(4928K)====>表示年轻代GC占用的内存是4416K内存,GC之后占用512K内存总大小4928K;0.0046102 secs =====>表示的是GC所用的时间单位毫秒 ;
-XX:+PrintGCDetails 打印垃圾回收的详细信息

1.串行垃圾收集器

指的是单线程进行的垃圾回收,垃圾回收的时候只有一个线程在进行工作,并且java应用中的所有的线程都需要进行暂停,等待垃圾回收的完成这个现象叫做STW-----这个应用的场景特别少
在程序运行的过程中添加2个参数即可
1.-XX:+UseSerialGC(指定的年轻代和老年代都使用串行垃圾收集器)
2.-XX:+PrintGCDetails(打印垃圾回收的详细信息)
在这里插入图片描述

//设置堆的初始和最大内存值为16M  串行收集器
-XX:+UseSerialGC -XX:+PrintGCDetails -Xms16m -Xmx16m

2.并行垃圾收集器

并行的垃圾收集器再创航的垃圾收集器的基础上做的改进,将单线程改为多线程的垃圾回收,这样缩短回收的时间,这里的手机过程也是会暂停应用程序的


1.ParNew垃圾收集器
ParNew垃圾收集器是工作再年轻代上的,只是将串行的垃圾收集器改为并行的
通过:-XX:+UseParNewGC参数设置年轻代使用ParNew回收期,老年代使用的依然是串行收集器
在这里插入图片描述


2.ParallelGC垃圾收集器
ParallelGC收集器工作机制和ParNew收集器一样,只是在此基础上,新增了2个和系统吞吐量相关的参数,使得其使用起来更加的灵活和高效
相关参数:
-XX:UseParallelGC(年轻代使用ParallelGC垃圾回收期,老年代使用串行回收器)
-XX:UseParallelOldGC(年轻代使用ParallelGC垃圾回收期,老年代使用ParallelOld垃圾回收器)
-XX:MaxGCPauseMillis(设置最大的垃圾收集时候的停顿时间,单位毫秒,需要注意的是ParallelGC为了达到设置的停顿时间,可能会调整堆的大小或者其他的参数,如果堆的大小设置的比较小,就会导致GC工作变得很频繁,反而可能会影响到性能,这个参数使用的时候需要谨慎处理)
-XX:GCTimeRatio(设置垃圾回收时间占程序运行时间的百分比,公式:1/(1+n),他的值为0~100之间的数字,默认值为99,也就是垃圾回收事假不能超过1%)
-XX:UseAdaptiveSizePolicy(自适应GC模式,垃圾回收器将会自动的调整新生代,老年代等参数,达到吞吐量,堆大小,停顿时间之间的平衡;一般用于手动的调整比较困难的场景,让收集器自动的进行调整)

在这里插入图片描述

2.CMS垃圾收集器

是一款并发,使用标记清除算法的垃圾回收器该回收器是针对老年代垃圾回收的,通过参数-XX:+UseConcMarkSweepGC进行设置的
CMS垃圾回收器的执行过程如下
在这里插入图片描述
初始化标记:标记root,会导致stw(程序暂停上面有解释)
并发标记,与用户线程同时运行
预清理与用户线程同时运行
重新标记,会导致stw
并发清除,与用户线程同时运行
调整堆大小,设置CMS再清理之后进行内存压缩,目的是清理内存中的碎片
并发重置状态等待下次CMS的触发,与用户线程同时运行
在这里插入图片描述

3.G1垃圾收集器(重点)jdk1.7开始1.9默认的回收器

G1的设计原则就是简化jvm性能调优,三步调优
1.开启G1垃圾收集器
2.设置堆的最大内存
3.设置最大的停顿时间
三种模式Young GC ,Mixed GC 和Full GC


相比于其他的垃圾收集器,最大的区别在于他取消了年轻代,老年代的物理划分,取而代之的是将堆划分为若干个区域,这些区域中包含了有逻辑上的年轻代和老年代区域
这样做的好处就是,不需要单独的空间对每一个代进行设置,不需要担心每个代内存是否足够的问题
在这里插入图片描述
在G1划分的区域中,年轻代的垃圾收集器依然是采用stw的方式,将存活对象拷贝到老年代或者Survivor空间,G1收集器通过将对象从一个区域复制到另外一个区域完成清理的工作
这就意味着在正常处理过程中,G1完成了堆的压缩,这样也就不会有cms内存碎片的问题存在了
Humongous
1.如果一个对象占用的空间超过了分区容量的50%以上,G1收集器就认为这是一个巨型对象
2.这些巨型对象,默认直接会被分配到老年代,但是如果他是一个短期存在的巨型对象,就会对垃圾收集器造成负面的影响
3.为了解决这个问题,G1划分了一个Humongous区,他是专门的存放巨型对象的,如果一个H村放不下巨型对象,那么就会寻找连续的H分区来进行存储,为了能找到连续的H区有时候就得启动Full GC

Young GC模式

在这里插入图片描述

主要是对Eden区进行GC,他在Eden空间耗尽时候会被触发
1.Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升为老年代空间
2.Survivor区的数据移动到新的Survivor区中,也会有部分的数据晋升为老年代空间中
3.最终Eden空间的数据为空,GC停止空间,应用线程继续执行

Mixed GC

当越来越多的对象晋升为老年代old region的时候,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,既 Mixed GC 这个算法并不是一个old GC 除了回收整个Young Region ,还会回收一部分的Old Region,这里需要注意的是:是一部分的老年代,而不是全部的老年代,可以选择那些old region进行收集,从而可以垃圾回收的耗时时间进行控制;也需要注意的是Mixed GC并不是Full GC
触发机制:参数:-XX:InitiatingHeapOccupancyPercent=n决定的,默认45%(当老年代大小占堆大小百分比达到这个阀值的时候触发)


GC2步:
1.全局并发标记
2.拷贝存活对象(evacuation)


全局并发标记5步
1.初始标记:标记从根节点直接到达对象,这个阶段会执行一次年轻化GC,会产生全局停顿
2.根区域扫描
G1 GC在初始标记的存活区扫描老年代的应用,并标记被引用的对象
该阶段与引用程序(非STW)同时运行,并且只有完成这个阶段的之后才能开始下一次STW年轻代垃圾回收
3.并发标记
G1 GC在整个堆查找可访问(存活的)对象,这个阶段与应用程序是同时运行的,可以被STW年轻代垃圾回收中断
4.重新标记
这个阶段指的是STW回收,因为程序再运行,针对上一次的标记正在修正
5.清除垃圾
清点和重置标记状态,这个阶段会STW,这个阶段并不会实际上去做垃圾的收集,等待evacuation阶段来回收
拷贝存活对象
evacuation阶段是全暂停的,这个阶段把一部分的Region里面的活对象拷贝到另一部分Region中,从而实现垃圾的回收清理在这里插入图片描述
在这里插入图片描述在这里插入图片描述

可视化的GC日志分析工具

日志打印在这里插入图片描述
生成log文件在这里插入图片描述
GC Easy可视化工具网址
在这里插入图片描述在这里插入图片描述

相关推荐

  1. Spring复习--2024.1/26更新

    2024-01-25 18:28:01       62 阅读
  2. C复习-查缺补漏-更新

    2024-01-25 18:28:01       63 阅读
  3. SQL语言复习-----1

    2024-01-25 18:28:01       44 阅读
  4. Pytorch 复习总结 1

    2024-01-25 18:28:01       42 阅读
  5. 复习Day1

    2024-01-25 18:28:01       37 阅读
  6. 13.复习1笔记

    2024-01-25 18:28:01       28 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-25 18:28:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-25 18:28:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-25 18:28:01       82 阅读
  4. Python语言-面向对象

    2024-01-25 18:28:01       91 阅读

热门阅读

  1. Spring-注解开发

    2024-01-25 18:28:01       61 阅读
  2. ChatGPT 和文心一言哪个更好用?

    2024-01-25 18:28:01       60 阅读
  3. Abaqus许可分析方法

    2024-01-25 18:28:01       60 阅读
  4. leetcode-200-岛屿问题

    2024-01-25 18:28:01       62 阅读
  5. Vue3生命周期和Vue2生命周期对比

    2024-01-25 18:28:01       61 阅读
  6. C语言之初级指针

    2024-01-25 18:28:01       59 阅读
  7. python ast 解析enum为C头文件

    2024-01-25 18:28:01       57 阅读
  8. doris安装文档翻译-标准部署(Standard deployment)

    2024-01-25 18:28:01       47 阅读
  9. webpack笔记

    2024-01-25 18:28:01       54 阅读