JAVA中的垃圾回收

JVM规范说了并不需要必须回收方法区,不具有普遍性,永久代使用的是JVM之外的内存

引用计数:效率要比可达性分析要强,随时发现,随时回收,实现简单,但是可能存在内存泄漏

局部变量表,静态引用变量,通过引用链关联的引用链是不会被回收,局部变量表天然作为GCROOTS

1,虚拟机栈中引用的对象(栈帧中的本地方法表)。
2,方法区中(1.8称为元空间)的类静态属性引用的对象,一般指被static修饰的对象,加载类的时候就加载到内存中。
3,方法区中的常量引用的对象。
4,本地方法栈中的JNI(native方法)引用的对象。
注意:即使可达性算法中不可达的对象,也不是一定要马上被回收,还有可能被抢救一下要真正宣告对象死亡需经过两个过程。
1.可达性分析后没有发现引用链
2.查看对象是否有finalize方法,如果有重写且在方法内完成自救[比如再建立引用],还是可以抢救一下,注意这边一个类的finalize只执行一次,这就会出现一样的代码第一次自救成功第二次失败的情况。[如果类重写finalize且还没调用过,会将这个对象放到一个叫做F-Queue的序列里,这边finalize不承诺一定会执行,这么做是因为如果里面死循环的话可能会时F-Queue队列处于等待,严重会导致内存崩溃,这是我们不希望看到的。

就是只是进行新生代回收的时候老年代的引用也可以作为GCROOTS

public class Test {public static Test obj;//这是一个类变量//    @Override
//    protected void finalize() throws Throwable {
//        System.out.println("调用当前链上的finalize方法");
//        obj=this;//当前带回收的对象在finalize方法上和一个引用链上面的对象建立了联系
//    }public static void main(String[] args) throws InterruptedException {obj=new Test();//对象第一次拯救自己obj=null;System.gc();//调用垃圾回收器System.out.println("第一次GC");//因为finalizer线程优先级很低,暂停2s来等待他Thread.sleep(3000);if(obj==null){System.out.println("对象已经死了");}else{System.out.println("对象还活着");}obj=null;System.gc();//调用垃圾回收器System.out.println("第二次GC");//因为finalizer线程优先级很低,暂停2s来等待他Thread.sleep(3000);if(obj==null){System.out.println("对象已经死了");}else{System.out.println("对象还活着");}}
}
垃圾回收算法:

任何时候都可能,当系统觉得你内存不足了就会开始回收常见的比如分配对象内存不足时这里的内存不足有可能 不是占用真的很高,可能是内存足够,但是没有连续内存空间去放这个对象,当前堆内存占用超过阈值时,手动 调用 System.gc() 建议开始GC时,系统整体内存不足时等

标记是非垃圾的对象就是可达的对象,然后清除清楚的是垃圾对象,要先递归进行遍历所有可达对象,然后清除的时候需要再开始遍历一遍,还需要进行维护空闲列表

就比如说我们的硬盘,只要你不小心点击了格式化,此时也不是真正的进行格式化,只是标记性删除,但是千万不要再向里面存放数据,因为数据会覆盖,就不好恢复了

复制算法:内存利用率贼低

首先经过可达性分析在A区找到可达的对象,一旦找到了可达的对象就不需要进行标记,直接将可达的对象进行复制算法放到另一块区域B,这是另一块空间的所有区域B的对象都是连续的

缺点:维护引用和对象的地址映射

回收的对象比较少,存活的对象比较多,那么移动的对象比较多,但是还要大量维护指针和对象的关系,老年代不适合使用复制算法,因为很多对象都不死,老年代复制对象开销太大

标记整理算法:

还要移动位置,还要修改引用对象关系很麻烦,这个算法比标记清除算法效率还低

分代回收:

新生代:老年代=1:2,edin区:幸存者1区:幸存者2区:

标记的开销和存活的对象成正比,因为标记只能标记存活的对象

清除阶段要进行全堆空间线性的遍历

压缩或者是整理和存活对象的大小成正比

Stop The World:

先确定GCROOTS,枚举根节点,此时要进行Stop The World,确保数据的一致性

stop the world停止的是用户线程,就是为保证一致性

可达性分析算法中枚举根节点会导致所有Java执行线程停顿

衡量一个垃圾回收器的标准就是吞吐量和低延迟

增量收集算法:

比如说我现在有一个房子,我一直不进行清理,一直制造垃圾,直到三个月之后才清理一次,此时清理的时间就比较长,阻隔用户线程的时间就比较长,但是如果说隔一会清理一会效果就会比较好,用户线程和回收线程协调交替执行,看样子就是并发的执行从而到达一种低延迟的行为,就是为了让用户感觉好一点;

被STW中断的应用程序线程会在完成GC之后恢复,频繁的中断会让用户感觉像是网速不快造成的电影卡顿一样

CMS自称低延迟,开发中不要显示的进行GC,导致STW

分区算法:降低停顿时间,主要是保证低延迟而不是吞吐量

有的分区存放大对象,有的区域存放小对象,回收区域的个数取决于时间的长短,可以控制可控时间

 
System.gc():提醒JVM垃圾回收器去执行GC,但是不确实马上执行GC,底层是调用RunTime().getTime().gc(),进而也不能确定finlizle方法一定会被调用

Full GC 就是收集整个堆,包括新生代,老年代和方法区,但是调用System.gc系统做了免责声明,GC具体干不干,不怪这个方法,仅仅是提醒JAVA虚拟机进行垃圾回收,但是实际上是否进行垃圾回收System.GC()不保证

做性能测试之前先进行GC,局部变量表第一个位置存放的是this

不会触发垃圾回收

buffer作用域已经过了,所以buffer肯定用不上了,系统会判定buffer占用的slot为可覆盖的slot,一但value覆盖buffer所在的槽,buffer引用被覆盖,此时没有任何引用指向字节对象数组,所以此时触发System.gc就会回收垃圾

内存溢出和内存泄漏:

空间不够GC之后空间还不够才会报OOM

如果动态加载很多类,intern方法调用太多,都会造成OOM,本地内存报OOM的情况相对来说比较少一些

1)宽泛意义上的内存泄漏:其实可以把对象定在方法内部作为局部变量,当方法执行完成以后,对象就要被回收了,但是如果将这个变量成员变量,那么这个对象的存活周期就变得很长,但是如果是这个变量静态变量还是大对象,类变量,随着类的加载而加载随着类的消亡而消亡,也会理解成宽泛的内存泄露,还比如说Session会话,不使用就没有必要存放,内存泄漏可能会导致内存溢出,如果出现很多生命周期很长的对象再加上很多没有办法回收的数据的存在就很有可能造成内存泄露;

2)存在很多生命周期很长的对象,而本身生命周期没有这么长的对象而又生命很长可以称之为是内存泄漏;

严格意义+宽泛意义上的内存泄漏,有些对象不使用,但是还存着引用链,有可能忘记断开引用,如右图,对象没有用处,但是还存在引用链,可能造成内存回收,ThreadLocal

1)单例模式中的对象是static,单例对象的生命周期和应用程序是一样长的,一个进程只有这一个实例,就比如说RunTime实例,声明的是静态的,每一个进程只有一个实例会随着程序的执行而产生,随着进程的结束而销毁,如果此时单例对象关联了一个外部的很大的对象,这个外部对象用一会就不用了,单例对象的生命周期非常长,但是这个引用关系又断不掉,所以此时连带着外部对象的生命周期也很长,本身又不用,但是无法释放内存,此时这个对象还无法回收,引用链条得不到释放;

2)当内部资源外部资源需要交互的时候,是需要进行手动的关闭资源链接,没有关闭资源,GC就无法回收这些对象,只有当程序结束的时候才能回收这些链接的对象,此时可能就会发生内存泄漏;

程序中的并行和并发(一个CPU快速切换CPU,不是真正意义上的同时执行):

并行就是在具体某一个时刻的时候有三个线程同时的进行执行,主要是取决于多核CPU,而并发在某一个时间点上面只能有一个进程在执行,在时间段内是可能有多个线程

垃圾回收器:

下面的绿线表示用户线程,红色表示垃圾回收器,串行垃圾回收器很慢

 
安全点:
 

点比较少,这个时候GC等待时间太长,用户线程执行时间过长,还有可能会导致OOM

点太多,STW时间也会变长,这个时候切换线程开销很大

最好是在跳转的时候或者调用新方法的时候,执行时间比较长,比如说在进行方法调用的时候,要将方法压入虚拟机栈,把它们作为savepoint,最好不要在程序指令执行很快的时候设置saveponint,sleep和block引用关系也不会发生变化;

线程处于睡眠或者是阻塞状态,这个线程无法响应JVM中断请求,此时你在让去走到中断安全点挂起是不可能的,不可能唤醒吧;

安全区的对象引用关系不会发生变化,因此安全区域发生GC,程序依然会继续执行, 要出安全区域了但GC没有结束,程序会等待,安全区域应该是并发的,但不能走出安全区域;

枚举出整个“GC Roots”是非常麻烦的,首先运行时数据本身就是动态的,在这个枚举的过程中必须保证其原子性,并且在今天 Java应用越来越大的情况下,单单一个方法区就有可能数百上千兆,里边的类、常量等等更是多,如果要逐个找出这些 根节点实在是一个非常非常耗时的事情。

那 JVM 是如何解决的呢?

首先在进行根节点选举时,必须暂停全部的用户线程,把这个过程称为“Stop The Word”(下面简称STW,但必须要说明,STW不一定是全局的,也可以是局部的,这和安全点的类型有关。此时说的必须暂停全部用户线程只是因为GC时必须使全部线程进入安全点

在HotSpot的解决方案中,是使用一组称为OopMap的数据结构来存放这些对象的引用OopMap在类加载动作完成时生成,也就是说当用户线程暂停下来之后,其实并不需要一个不漏的检查完所有的执行上下文和全局的引用位置,而是直接通过OopMap来获取栈上或寄存器里哪里有GC管理的指针引用指针

安全点

OopMap解决了一部分问题,但没有解决所有的问题,试想一下,对象中的引用关系并非一成不变,如果每次执行一条字节码指令都去生成一个OopMap,那就必须消耗大量额外的存储空间,为了解决这个问题HotSpot并没有让每条指令都生成OopMap,而是只在特定的位置生成OopMap,这个位置就被称为安全点,放置安全点的位置一般是以“是否具有让程序长时间执行特征”为标准进行选定

引用:前提是引用关系在的情况下,都是可达的,没有引用关系啥引用都会被回收

强引用:如果发生OOM,也不会回收

软引用:内存不足回收,内存充足就不会回收,主要用于缓存,当我们第一次进行深度优先遍历之后把那些不可达的对象垃圾回收之后,发现空间还是不够,于是就会尝试把可达的软引用所指向的对象进行回收,也叫做二次回收,如果回收了软引用空间还不够,就会报OOM

但是强引用才会导致内存溢出,软引用是不会导致内存溢出的

弱引用:只要进行GC,就会进行垃圾回收,主要用于缓存

虚引用:唯一目的就是对象回收的跟踪

 

软引用:内存足够不会回收可触及的也就是软引用可达的对象,当堆内存不够的时候,才会回收,软引用是不会报内存溢出的,Mybatis内部缓存就是用到软引用

此时如果说刚好能容得下大数组,大对象,还刚好容不下这个弱引用,不一定说回收软引用就会发生OOM,不一定报OOM之前才会回收

   public static void main(String[] args) throws InterruptedException {//声明一个强引用,将强引用的对象放入到软引用的形参里面Object s1=new Object();SoftReference<Object> reference=new SoftReference<>(s1);//创建软引用s1=null;//但是此时s1所指向的对象既有强引用s1的关联,又存在着弱引用的reference的关联,所以此时应该把强引用的关联解除,也就是让s1里面不指向任何数据,保证这个对象只有一个软引用关联System.out.println(reference.get());//防止强引用来干扰}
import java.lang.ref.SoftReference;
class User{public String username;public String password;public User(String username,String password){this.username=username;this.password=password;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +'}';}
}
public class Test {public static void main(String[] args) throws InterruptedException {//声明一个强引用,将强引用的对象放入到软引用的形参里面User user=new User("李四","12503487");SoftReference<Object> reference=new SoftReference<>(user);//创建软引用user=null;//但是此时s1所指向的对象既有强引用s1的关联,又存在着弱引用的reference的关联,所以此时应该把强引用的关联解除,也就是让s1里面不指向任何数据//上面的三行代码等价于 SoftReference<Object> reference=new SoftReference<>(new User("李四","12503487"))//1.从软引用中可以获取到软引用对象的实例,可以获取到强引用的实例,由于堆空间内存足够,所以不会回收软引用的可达对象System.out.println(reference.get());//2.创建一个大对象try {int[] array = new int[10000 * 100000];}catch (Throwable e){e.printStackTrace();}System.gc();System.out.println(reference.get());//在报OOM的时候,垃圾回收器会回收软引用的可达对象}
}

弱引用:非必需的对象

软引用不如弱引用回收的快,因为软引用要使用算法判断内存是否不足

虚引用:一旦将弱引用回收,就会将虚引用存放到引用队列中,可以追踪垃圾回收过程,虚引用的作用主要是将回收的对象放在队列中 进行GC对象追踪

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;public class Test{public static Test test;//对当前对象的声明public static ReferenceQueue<Test> queue=null;//声明引用队列public static class WorkThread extends Thread{@Overridepublic void run() {while (true) {if (queue != null) {PhantomReference<Test> reference = null;try {//如果这个对象被回收了,那么虚引用会被放到等待队列里面reference = (PhantomReference<Test>) queue.remove();} catch (InterruptedException e) {e.printStackTrace();}if (reference != null) {System.out.println("追踪垃圾回收过程:当前Test实例被GC了");}}}}public static void main(String[] args) {Thread t=new WorkThread();t.setDaemon(true);//设置为守护线程:当程序中没有非守护线程时,守护线程也就执行结束t.start();//1.创建虚引用队列queue=new ReferenceQueue<>();Test test=new Test();//2.创建Test 对象的虚引用PhantomReference<Test> ref=new PhantomReference<>(test,queue);test=null;//3.尝试获取到虚引用中的对象获取失败,因为虚引用的对象不可以被获取到System.out.println(ref.get());System.gc();//4.执行GC之后,虚引用引用的对象会被回收,此时会把虚引用放入到引用队列里面}}
}

不同的引用类型主要是取决于不同对象的可达性状态和对象垃圾收集的影响,强引用就是普通对象的一个引用,只要有一个强引用指向一个对象就表示这个对象还活着,垃圾回收器就永远无法回收这一类的对象,只有没有其他引用关系或者是超过了引用的作用域或者是显示将引用设置为null,才会进行垃圾回收;

软引用:只有当JVM认为内存不足的时候才会进行试图回收引用所指向的对象,软引用主要适用于实现内存敏感的缓存,如果还有空闲内存,就可以暂时去保留缓存,当内存不足的时候会清理掉,这样就可以保证使用缓存的同时,不会耗尽内存

弱引用是相对于强引用关联的,不管内存是否足够都会回收弱引用

虚引用不会决定对象的生命周期,它提供了一种确保对象被finlize之后,去做某些事情的一种机制,当垃圾回收器准备去回收一个对象的时候,如果发现她还存在虚引用,就会在回收对象的内存之前,就会把这个虚引用加入到与之关联的引用的队列里面,那么程序可以通过判断引用队列是否已经加入了虚引用来去了解被引用的对象是否要进行垃圾回收,然后就可以在引用对象被回收之前来采取必要的一个行动;

终结器引用:finalReference

吞吐量=a/a+b这个数值越大越好,垃圾收集开销=b/a+b这个值越小越好

暂停时间=STW时间

收集频率:回收的频率低,不代表一次GC的时间短,大学洗衣服

一次赞一快洗(时间比较长)VS经常洗(一天一洗,时间比较短),频率越高,STW时间短一点

1)吞吐量:吞吐量越大越好,就是用户线程所消耗的时间在整个JVM生命周期中所占用的时间越长,那么垃圾频率就越低,但是每一次执行GC,那么用户线程停止,STW时间就越长(类比于洗衣服),一次性的暂停时间就很长,单位时间内用户线程做的事情更多

2)低延迟:注重每一次的暂停时间变短,用户线程暂停时间短,那么垃圾回收GC的频率就越高,因为暂停时间短,每一次GC都收集不了多少垃圾,线程频繁切换也需要时间,每一次本来就注重低延迟,要求GC垃圾回收短,况且线程上下文切换还消耗时间,每一次GC垃圾又回收不了多少,那么最终一共的STW时间肯定会比吞吐量的STW时间长(类比于洗衣服);

3)高吞吐量和低延迟是矛盾的

这就类似于洗衣服,从宿舍去水房的时间和从水房回到宿舍的时间就类似于线程切换

和用户交互的程序,延迟要短一些,争取在垃圾回收的过程中多线程回收

有的是服务器端,吞吐量要高一些

G1垃圾回收器就是可以保证在给定停顿时间的基础上,尽量的提高吞吐量

JDK7之前,实线,Serial OLD GC是CMS的后备方案

在JDK9中取消了红线组合

在JDK14中绿线会被删除

CMS和PSGC框架不同,不可以一起使用,PNGC和PSGC性能差不多

CMS:不能是在老年代空间满的时候进行使用,需要提前进行回收,因为CMS是并发的,他在回收的时候用户线程还在执行,用户线程还有可能制造新的垃圾,所以需要提前进行回收,那如果说回收的比较晚,垃圾制造的速度比回收的速度还要快,可能CMS回收失败一旦失败,所以要使用SOGC作为备用方案,赶紧把用户线程停下来进行全部GC

单核CPU是单线程垃圾收集器比多线程垃圾收集器要高,因为防止进行大量的线程切换

Serial(新生代)+Serial Old(老年代):单线程垃圾回收:

当新生代使用serial的时候老年代默认使用Serial Old,他们在执行的时候必须停止所有的用户线程

-XX:PrintCommandLineFlags -XX:+UseSerialGC,表明新生代使用Serial GC,老年代使用Serial Old GC

然后可以通过jps验证一下,jinfo -flag UseSerialGC +进程的ID

缺点:串行垃圾回收器会导致验证的STW

parNew新生代并行垃圾回收器+和Serial Old(单线程穿行垃圾回收器)或者是CMS(老年代并行垃圾回收器)一起使用
 

在服务器端模式下是多核CPU的场景,这个时候就不和客户端一样是一个单线程的垃圾回收器了,服务器端硬件更多一些

在老年代可以使用CMS或者是Serial Old,在JDK9中Serial Old不能再和ParewNew使用了,在JDK14CMS也被移除了,这个时候ParNew就比较尴尬了

对于新生代,使用多线程垃圾回收器,使得GC的时间更短,垃圾回收更高效STW时间更短

但是在老年代,标记整理算法效率比较差,涉及到内存碎片整理

单CPU:同一时刻只能由一个线程执行

设置线程数量不要超过CPU核数,放置多个线程抢夺CPU,和CPU核数相同越好

-XX:PrintCommandLineFlags -XX:+UseParNewSerialGC -XX:+UseConcMarkSweepGC

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

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

相关文章

利达卓越:发挥金融力量,促进团队发展

随着中国经济的快速增长和金融改革的逐步深化&#xff0c;我国金融业取得了令人瞩目的发展。作为经济的重要支柱&#xff0c;我国金融业的规模和实力不断扩大&#xff0c;已经成为全球最大的金融市场之一。利达卓越是一支由管理精英组成的团队&#xff0c;专注于金融行业的投资…

Kafka消费者使用案例

本文代码链接&#xff1a;https://download.csdn.net/download/shangjg03/88422633 1.消费者和消费者群组 在 Kafka 中&#xff0c;消费者通常是消费者群组的一部分&#xff0c;多个消费者群组共同读取同一个主题时&#xff0c;彼此之间互不影响。Kafka 之所以要引入消费者群组…

HTTP 响应头 X-Frame-Options

简介 X-Frame-Options HTTP 响应头用来给浏览器一个指示。该指示的作用为&#xff1a;是否允许页面在 <frame>, </iframe> 或者 <object> 中展现。 网站可以使用此功能&#xff0c;来确保自己网站的内容没有被嵌套到别人的网站中去&#xff0c;也从而避免了…

【linux】E45: ‘readonly‘ option is set (add ! to override)

vim 编辑文件保存时 E45:设置了“只读”选项&#xff08;添加&#xff01;以覆盖&#xff09; 输入&#xff1a; wq! 提示 "/etc/my.cnf" E212: Cant open file for writing 依然是没有权限&#xff1a; 解决一&#xff1a; 切换用户&#xff1a; su root 解…

黑马店评-04缓存更新策略,保证MySQL数据库中的数据和Redis中缓存的数据一致性

缓存更新策略(数据一致) 更新策略 缓存更新是Redis为了节约内存而设计出来的机制,当我们向Redis插入太多数据时就会导致缓存中的数据过多,所以Redis会对部分数据进行更新即淘汰 低一致性需求(数据长久不发生变化): 使用内存淘汰机制,例如店铺类型信息的查询缓存,因为这部分…

Rust初接触

一、什么是Rust Rust 是由 Mozilla 开发的多范式编程语言&#xff0c;专注于性能和安全性。 Rust 以其先进的安全并发能力而闻名&#xff0c; 它的语法类似于 C&#xff0c;但它提供了更快的速度和内存安全性&#xff0c;但不使用垃圾收集器。 Rust 最初是为 Mozilla Firefox …

SpringCloud组件Ribbon的IRule的问题排查

最近很久没有写文章啦&#xff0c;刚好遇到了一个问题&#xff0c;其实问题也挺简单&#xff0c;但是还是得对源码有一定了解才能够发现。 最近在实现一个根据请求流量的标签&#xff0c;将请求转发到对应的节点&#xff0c;其实和俗称的灰度请求有点相似&#xff0c; 实现思…

数据结构与算法-(8)---队列(Queue)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

【AI视野·今日Robot 机器人论文速览 第五十三期】Thu, 12 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Thu, 12 Oct 2023 Totally 25 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Pixel State Value Network for Combined Prediction and Planning in Interactive Environments Authors Sascha Rosbach, St…

eclipse 配置selenium环境

eclipse环境 安装selenium的步骤 配置谷歌浏览器驱动 Selenium安装-如何在Java中安装Selenium chrome驱动下载 eclipse 启动配置java_home&#xff1a; 在eclipse.ini文件中加上一行 1 配置java环境&#xff0c;网上有很多教程 2 下载eclipse&#xff0c;网上有很多教程 ps&…

207、SpringBoot 整合 RabbitMQ 实现消息的发送 与 接收(监听器)

目录 ★ 发送消息★ 创建队列的两种方式代码演示需求1&#xff1a;发送消息1、ContentUtil 先定义常量2、RabbitMQConfig 创建队列的两种方式之一&#xff1a;配置式&#xff1a;问题&#xff1a; 3、MessageService 编写逻辑PublishController 控制器application.properties 配…

思维模型 峰终定律

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。 1 峰-终定律的应用 1.1 迪士尼游乐园 迪士尼乐园采用了多种策略来创造令人难忘的体验&#xff0c;从而遵循峰终定律的原则。具体如下&#xff1a; 迪士尼乐园的入口设计和服务体验&…

基于workbench的PTFE矩形密封圈压缩回弹仿真分析

研究背景&#xff1a; 近年来随着工业发展和科技进步&#xff0c;高压容器使用场景逐渐增大&#xff0c;使用环境越发苛刻&#xff0c;如高温、高压以及内部压力的波动&#xff0c;这都对容器端面密封性能的要求更为严格。端面密封所用的密封件必须具备优良的回弹性能和耐化学…

【Vue基础-数字大屏】加载动漫效果

一、需求描述 当网页正在加载而处于空白页面状态时&#xff0c;可以在该页面上显示加载动画提示。 二、步骤代码 1、全局下载npm install -g json-server npm install -g json-server 2、在src目录下新建文件夹mock&#xff0c;新建文件data.json存放模拟数据 {"one&…

推荐《金田一少年事件簿》

天树征丸原作&#xff0c;佐藤文也作画的漫画 金田一少年事件簿 播报编辑讨论7上传视频 《金田一少年事件簿》是一部日本推理漫画。早期原作为金成阳三郎&#xff08;后担任剧本&#xff09;&#xff0c;原作为天树征丸&#xff08;前原案&#xff09;&#xff0c;由漫画家佐…

【教程】使用vuepress构建静态文档网站,并部署到github上

官网 快速上手 | VuePress (vuejs.org) 构建项目 我们跟着官网的教程先构建一个demo 这里我把 vuepress-starter 这个项目名称换成了 howtolive 创建并进入一个新目录 mkdir howtolive && cd howtolive使用你喜欢的包管理器进行初始化 yarn init 这里的问题可以一…

2023-2024-1 for循环-1(15-38)

7-15 输出闰年 输出21世纪中截止某个年份以来的所有闰年年份。注意&#xff1a;闰年的判别条件是该年年份能被4整除但不能被100整除、或者能被400整除。 输入格式: 输入在一行中给出21世纪的某个截止年份。 输出格式: 逐行输出满足条件的所有闰年年份&#xff0c;即每个年…

前端TypeScript学习day04-交叉类型与泛型

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 交叉类型 泛型 创建泛型函数 调用泛型函数&#xff1a; 简化调用泛型函数&#xff1a; 泛型约束 指定…

Marin说PCB之BGA焊盘削焊盘带来的焊接问题和解决办法

每周日上午10点钟都是小编最开心的时间了&#xff0c;这个点是斗破苍穹播出的时间。小编我从萧炎从这个动漫开播到现在都追了好多年了&#xff0c;强烈推荐喜欢这个小说的可以看这个动漫&#xff0c;拍的还不错&#xff0c;只是萧炎的配音不再是张沛老师了&#xff0c;有点可惜…

基于Java的宠物领养管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…