JVM——GC垃圾回收机制

news/2024/5/14 5:19:22/文章来源:https://blog.csdn.net/wdays83892469/article/details/126908059

文章目录

  • JVM——GC垃圾回收机制
    • 一、如何判断哪些对象应该被回收——对象判活算法
      • 引用计数算法
      • 可达性分析算法
      • 引用
      • 最终判定
    • 二、对象应该怎么被回收——垃圾回收算法
      • 分代收集理论
      • 标记-清除算法
      • 标记-复制算法
      • 标记-整理算法
    • 三、内存对象什么时候被回收——触发条件
      • 年轻代GC(Minor GC) 触发条件:
      • 老年代GC (Major GC)触发机制:
      • 整堆收集 (Full GC)触发机制:
    • 四、垃圾收集器
      • Serial收集器
      • ParNew收集器
      • Parallel Scavenge/Parallel Old收集器
      • CMS收集器
      • Garbage First (G1收集器)
      • 如何选择合适的垃圾收集器呢

JVM——GC垃圾回收机制

GC英文全称为Garbage Collection,即垃圾回收

Java与C++开发者之间有一堵深深的鸿沟

Java:内存申请和释放,这么重要的事情一定不能交给程序员自己去做

C++:内存申请和释放,这么重要的事情一定不能交给机器自己去做

有不少人把GC这项技术当作Java语言的伴生产物。事实上,垃圾收集的历史远远比Java久远,不过因为Java的出现,人们普遍开始认识到GC的可贵,自此多数脚本语言都具备了GC,实际上比将GC成为垃圾回收而言,称为“虚拟内存”更加适合,因为它的实质就是在较小的物理内存基础上,通过及时清理让程序得以申请更多的内存。

当内存管理自动化的现在,我们学习GC算法为的是当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就可以对这些“自动化”的技术实施必要的监控和调节。

如何判断哪些对象可以被回收,对象应该怎么被回收,对象所占内存在什么时候被回收是GC设计者所必须思考的问题。

承接上上篇,将JVM内存架构的一节,我们可以笼统的把JVM内存运行区分为堆和栈两个部分,GC发生的位置显然不会是随着线程出生入死的栈内存,因为栈内存随着线程结束就被回收,共享内存中才会滞留需要被清理的垃圾。

现在开始正式思考,GC设计的三个问题

一、如何判断哪些对象应该被回收——对象判活算法

引用计数算法

很多教科书判断对象是否存活的算法是这样的:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。很多应届生和一些有多年工作经验的开发人员,他们对于这个问题给予的都是这个答案。

客观地说,引用计数算法(Reference Counting)虽然占用了一些额外的内存空间来进行计数,但它的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法。比如Python就是引用计数算法进行内存管理的,但是主流的Java虚拟机里面都没有选用引用计数算法来管理内存,它存在的最大问题是无法解决循环引用的问题。

设置idea参数打印GC过程
在这里插入图片描述
在这里插入图片描述
加入参数-XX:+PrintGCDetails

循环引用代码示例

public class ReferenceCountingGC {public Object instance = null;private static final int _1MB = 1024 * 1024;/*** 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否有回收过*/private byte[] bigSize = new byte[2 * _1MB];public static void testGC() {ReferenceCountingGC objA = new ReferenceCountingGC();ReferenceCountingGC objB = new ReferenceCountingGC();objA.instance = objB;objB.instance = objA;objA = null;objB = null;// 假设在这行发生GC,objA和objB是否能被回收?System.gc();}public static void main(String[] args) {testGC();}}

打印结果节选

[0.208s][info   ][gc             ] GC(0) Pause Full (System.gc()) 14M->3M(20M) 8.606ms
[0.208s][info   ][gc,cpu         ] GC(0) User=0.00s Sys=0.02s Real=0.01s
[0.208s][info   ][gc,heap,exit   ] Heap
[0.208s][info   ][gc,heap,exit   ]  garbage-first heap   total 20480K, used 3160K [0x0000000600c00000, 0x0000000800000000)
[0.208s][info   ][gc,heap,exit   ]   region size 2048K, 1 young (2048K), 0 survivors (0K)
[0.208s][info   ][gc,heap,exit   ]  Metaspace       used 6308K, capacity 6379K, committed 6528K, reserved 1056768K
[0.208s][info   ][gc,heap,exit   ]   class space    used 549K, capacity 570K, committed 640K, reserved 1048576K

从运行结果中可以清楚看到内存回收日志中包含“14M->3M(20M)”,意味着虚拟机并没有因为这两个对象互相引用就放弃回收它们,这也从侧面说明了Java虚拟机并不是通过引用计数算法来判断对象是否存活的。

可达性分析算法

当前主流的商用程序语言(Java、C#)的内存管理子系统,都是通过可达性分析(Reachability Analysis)算法来判定对象是否存活的。这个算法的基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
在这里插入图片描述

如图3-1所示,对象object 5、object 6、object 7虽然互有关联,但是它们到GC Roots是不可达的,因此它们将会被判定为可回收的对象。

在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:

1.位于虚拟机栈的栈帧中的本地变量表中所引用到的对象(其实就是我们方法中的局部变量同样也包括本地方法栈中JNI引用的对象。
2.类的静态成员变量引用的对象(方法区) 常量池中引用的对象,譬如String里的引用(方法区)。
3.加有锁(synchronized关键字)的对象。

4.Java虚拟机内部的引用(如基本数据类型对应的Class对象,一些常驻的异常对象(比如
NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
5.反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等) 除了这些固定的GC
Roots集合以外,不同垃圾收集器不同回收的内存区域,还可以“临时”加入,共同构成完整GC Roots集合。

譬如后文将会提到的分代收集和局部回收(Partial GC),如果只针对Java堆中某一块区域发起垃圾收集时(如最典型的只针对新生代的垃圾收集),必须考虑到内存区域是虚拟机自己的实现细节(在用户视角里任何内存区域都是不可见的),更不是孤立封闭的,所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用,这时候就需要将这些关联区域的对象也一并加入GC Roots集合中去,才能保证可达性分析的正确性。
目前最新的几款垃圾收集器[1]无一例外都具备了局部回收的特征,为了避免GC Roots包含过多对象而过度膨胀,它们在实现上也做出了各种优化处理。

引用

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可达,判定对象是否存活都和“引用”离不开关系。在JDK 1.2版之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。

  1. 强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Objectobj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

  2. 软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。

public class Main {public static void main(String[] args) {//强引用写法:Object obj = new Object();//软引用写法:SoftReference<Object> reference = new SoftReference<>(new Object());//使用get方法就可以获取到软引用所指向的对象了System.out.println(reference.get());}
}

这里我们来进行一个测试,首先我们需要设定一下参数,来限制最大堆内存为10M,并且打印GC日志:

-XX:+PrintGCDetails -Xms10M -Xmx10M

接着运行以下代码:

public class Main {public static void main(String[] args) {ReferenceQueue<Object> queue = new ReferenceQueue<>();SoftReference<Object> reference = new SoftReference<>(new Object(), queue);System.out.println(reference);try{List<String> list = new ArrayList<>();while (true) list.add(new String("lbwnb"));}catch (Throwable t){System.out.println("发生了内存溢出!"+t.getMessage());System.out.println("软引用对象:"+reference.get());System.out.println(queue.poll());}}
}

运行结果如下:

java.lang.ref.SoftReference@232204a1
[GC (Allocation Failure) [PSYoungGen: 3943K->501K(4608K)] 3943K->2362K(15872K), 0.0050615 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 3714K->496K(4608K)] 5574K->4829K(15872K), 0.0049642 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 3318K->512K(4608K)] 7652K->7711K(15872K), 0.0059440 secs] [Times: user=0.03 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) --[PSYoungGen: 4608K->4608K(4608K)] 11807K->15870K(15872K), 0.0078912 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 4608K->0K(4608K)] [ParOldGen: 11262K->10104K(11264K)] 15870K->10104K(15872K), [Metaspace: 3207K->3207K(1056768K)], 0.0587856 secs] [Times: user=0.24 sys=0.00, real=0.06 secs] 
[Full GC (Ergonomics) [PSYoungGen: 4096K->1535K(4608K)] [ParOldGen: 10104K->11242K(11264K)] 14200K->12777K(15872K), [Metaspace: 3207K->3207K(1056768K)], 0.0608198 secs] [Times: user=0.25 sys=0.01, real=0.06 secs] 
[Full GC (Ergonomics) [PSYoungGen: 3965K->3896K(4608K)] [ParOldGen: 11242K->11242K(11264K)] 15207K->15138K(15872K), [Metaspace: 3207K->3207K(1056768K)], 0.0972088 secs] [Times: user=0.58 sys=0.00, real=0.10 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 3896K->3896K(4608K)] [ParOldGen: 11242K->11225K(11264K)] 15138K->15121K(15872K), [Metaspace: 3207K->3207K(1056768K)], 0.1028222 secs] [Times: user=0.63 sys=0.01, real=0.10 secs] 
发生了内存溢出!Java heap space
软引用对象:null
java.lang.ref.SoftReference@232204a1
HeapPSYoungGen      total 4608K, used 4048K [0x00000007bfb00000, 0x00000007c0000000, 0x00000007c0000000)eden space 4096K, 98% used [0x00000007bfb00000,0x00000007bfef40a8,0x00000007bff00000)from space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)to   space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)ParOldGen       total 11264K, used 11225K [0x00000007bf000000, 0x00000007bfb00000, 0x00000007bfb00000)object space 11264K, 99% used [0x00000007bf000000,0x00000007bfaf64a8,0x00000007bfb00000)Metaspace       used 3216K, capacity 4500K, committed 4864K, reserved 1056768Kclass space    used 352K, capacity 388K, committed 512K, reserved 1048576K

可以看到,当内存不足时,软引用所指向的对象被回收了,所以get()方法得到的结果为null,并且软引用对象本身被丢进了队列中。
3. 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
弱引用比软引用的生命周期还要短,在进行垃圾回收时,不管当前内存空间是否充足,都会回收它的内存。

我们可以像这样创建一个弱引用:

public class Main {public static void main(String[] args) {WeakReference<Object> reference = new WeakReference<>(new Object());System.out.println(reference.get());}
}// 使用方法和软引用是差不多的,但是如果我们在这之前手动进行一次GC:public class Main {public static void main(String[] args) {SoftReference<Object> softReference = new SoftReference<>(new Object());WeakReference<Object> weakReference = new WeakReference<>(new Object());//手动GCSystem.gc();System.out.println("软引用对象:"+softReference.get());System.out.println("弱引用对象:"+weakReference.get());}
}

可以看到,弱引用对象直接就被回收了,而软引用对象没有被回收。同样的,它也支持ReferenceQueue,和软引用用法一致,这里就不多做介绍了。

WeakHashMap正是一种类似于弱引用的HashMap类,如果Map中的Key没有其他引用那么此Map会自动丢弃此键值对。

public class Main {public static void main(String[] args) {Integer a = new Integer(1);WeakHashMap<Integer, String> weakHashMap = new WeakHashMap<>();weakHashMap.put(a, "yyds");System.out.println(weakHashMap);a = null;System.gc();System.out.println(weakHashMap);}
}

可以看到,当变量a的引用断开后,这时只有WeakHashMap本身对此对象存在引用,所以在GC之后,这个键值对就自动被舍弃了。所以说这玩意,就挺适合拿去做缓存的。
4. 虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。
虚引用相当于没有引用,随时都有可能会被回收。

看看它的源码,非常简单:

public class PhantomReference<T> extends Reference<T> {/*** Returns this reference object's referent.  Because the referent of a* phantom reference is always inaccessible, this method always returns* <code>null</code>.** @return  <code>null</code>*/public T get() {return null;}/*** Creates a new phantom reference that refers to the given object and* is registered with the given queue.** <p> It is possible to create a phantom reference with a <tt>null</tt>* queue, but such a reference is completely useless: Its <tt>get</tt>* method will always return null and, since it does not have a queue, it* will never be enqueued.** @param referent the object the new phantom reference will refer to* @param q the queue with which the reference is to be registered,*          or <tt>null</tt> if registration is not required*/public PhantomReference(T referent, ReferenceQueue<? super T> q) {super(referent, q);}}

也就是说我们无论调用多少次get()方法得到的永远都是null,因为虚引用本身就不算是个引用,相当于这个对象不存在任何引用,并且只能使用带队列的构造方法,以便对象被回收时接到通知。

最终判定

即使在可达性分析算法中判定为不可达的对象,也不是“非死不可”的,这时候它们暂时还处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。假如对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,那么虚拟机将这两种情况都视为“没有必要执行”。

还记得一开始学Object时候Object类的finalize()方法吗?

此方法正是最终判定方法,如果子类重写了此方法,那么子类对象在被判定为可回收时,会进行二次确认,也就是执行finalize()方法,而在此方法中,当前对象是完全有可能重新建立GC Roots的!所以,如果在二次确认后对象不满足可回收的条件,那么此对象不会被回收,巧妙地逃过了垃圾回收的命运。

笔者建议大家尽量避免使用它,因为这是Java刚诞生时为了使传统C、C++程序员更容易接受Java所做出的一项妥协。finalize()能做的所有工作,使用try-finally或者其他方式都可以做得更好、更及时,建议大家完全可以忘掉Java语言里面的这个方法。

二、对象应该怎么被回收——垃圾回收算法

前面我们介绍了对象存活判定算法,现在我们已经可以准确地知道堆中的哪些对象可以被回收了,那么,接下来就该考虑如何对对象进行回收了,垃圾收集器会不定期地检查堆中的对象,查看它们是否满足被回收的条件。

垃圾收集算法的实现涉及大量的程序细节,且各个平台的虚拟机操作内存的方法都有差异,在本节中我们暂不过多讨论算法实现,只重点介绍分代收集理论和几种算法思想及其发展过程。

分代收集理论

这也是八股文背的最严重的的一块,什么新生代老年代面试的时候背来背去却从来没有真正理解,首先确定一个概念,分代收集理论是建立在以下两个假说基础上的。

1)弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
2)强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。

在Java堆划分出不同的区域之后,垃圾收集器才可以每次只回收其中某一个或者某些部分的区域——因而才有了“Minor GC”“Major GC”“Full GC”这样的回收类型的划分;也才能够针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法——因而发展出了“标记-复制算法”“标记-清除算法”“标记-整理算法”等针对性的垃圾收集算法。这里笔者提前提及了一些新的名词,它们都是本章的重要角色,稍后都会逐一登场,现在读者只需要知道,这一切的出现都始于分代收集理论。

把分代收集理论具体放到现在的商用Java虚拟机里,设计者一般至少会把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域[2]。顾名思义,在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

标记-清除算法

最早出现也是最基础的垃圾收集算法是“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。标记过程就是对象是否属于垃圾的判定过程,这在前一节讲述垃圾对象标记判定算法时其实已经介绍过了。之所以说它是最基础的收集算法,是因为后续的收集算法大多都是以标记-清除算法为基础,对其缺点进行改进而得到的。它的主要缺点有两个:第一个是执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低;第二个是内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。标记-清除算法的执行过程如图3-2所示。

在这里插入图片描述

标记-复制算法

标记-复制算法常被简称为复制算法。为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销,但对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。这样实现简单,运行高效,不过其缺陷也显而易见,这种复制回收算法的代价是将可用内存缩小为了原来的一半,空间浪费未免太多了一点。(实现的时候我们可以根据具体内存情况分配以达到节省内存的目的)

在这里插入图片描述

标记-整理算法

标记-复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
针对老年代对象的存亡特征,标记-真理算法的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存,“标记-整理”算法的示意图如图3-4所示。
标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策:

在这里插入图片描述

我们可以将标记清除算法和标记整理算法混合使用,在内存空间还不是很凌乱的时候,采用标记清除算法其实是没有多大问题的,当内存空间凌乱到一定程度后,我们可以进行一次标记整理算法。

三、内存对象什么时候被回收——触发条件

年轻代GC(Minor GC) 触发条件:

a.当年轻代空间不足时,就会触发Minor GC,这里的年轻代指的是Eden区满的时候,Survivor满了不会引发GC(每次 Minor GC 会清理年轻代的内存

b.因为Java对象大多都具备朝生夕灭的特性,所以Minor GC 非常频繁,一般回收速度也比较块。

c.Minor GC 会引发STW,暂停其他用户的线程,等垃圾回收结束,用户线程才会恢复运行。

老年代GC (Major GC)触发机制:

a.指发生在老年的GC,对象从老年代消失时,我们说“Major GC”或“Full GC” 发生了。

b.出现Major GC,经常会伴随着至少一次的Minor GC(但非绝对的,在Parallel Scavenge 收集器的收集策略里就有直接进行Major GC的策略选择。)

就是在老年代空间不足时,会尝试触发Minor GC。如果之后空间还不足,则触发Major GC

c.Major GC的速度一般会比Minor GC 慢10倍 以上,STW的时间更长。

d.如果Major GC 后,内存还不够,就报OOM 了

整堆收集 (Full GC)触发机制:

a.调用System.gc()时,系统建议执行Full GC,但是不必然执行。

b.老年代空间不足。

c.方法区空间不足。

d.通过Minor GC后进入老年代的平均大小大于老年代可用内存。

e.由Eden区,survivor space0(From Space)区向survivor space1(To Space)区复制时,对象大于To Space可用内存,则把该对象转存倒老年代,且老年代的可用内存小于该对象大小。

说明:Full GC 是开发或调优中尽量要避免的,这样暂停时间会短一些

四、垃圾收集器

聊完了对象存活判定和垃圾回收算法,接着我们就要看看具体有哪些垃圾回收器的实现了。我们可以自由地为新生代和老年代选择更适合它们的收集器。

具体流程就暂不介绍了,列名字让大家有个印象,用的的时候再去细致了解

Serial收集器

ParNew收集器

Parallel Scavenge/Parallel Old收集器

CMS收集器

Garbage First (G1收集器)

如何选择合适的垃圾收集器呢

这个准则只能参考,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处理器的数量和速度

如果应用程序的内存在100M左右,使用串行收集器 -XX:+UseSerialGC。

如果是单核心,并且没有停顿要求,默认收集器,或者选择带有选项的-XX:+UseSerialGC

如果允许停顿时间超过1秒或者更长时间,默认收集器,或者选择并行-XX:+UseParallelGC

如果响应时间最重要,并且不能超过1秒,使用并发收集器 -XX:+UseConcMarkSweepGC or -XX:+UseG1GC

1.8默认的垃圾回收:PS + ParallelOld

参考资料:《深入理解Java虚拟机》第三版

JVM笔记(二)JVM内存管理 · 语雀

JVM垃圾收集—垃圾收集器及常见组合参数_向着百万年薪努力的小赵的博客

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

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

相关文章

如期而至的SVN服务器迁移引来一个大瓜XAMPP

文章目录前言方案评估前奏XAMMP搭建svn服务准备软件包安装必要环境和工具安装xampp运行xampp编辑xampp访问xampp安装subversion安装svnmanager创建svn仓库目录修改配置文件为svnmanager创建MySQL用户重启xammp服务访问svnmanager登录svnmanager可能遇到的错误查看服务器目录信息…

10 nginx 中的 slab

前言 这里主要是描述 nginx 中的 slab 内存分配相关 slab 在很多的地方都有使用, 比如 linux, nginx, netty 等等 主要的作用是 内存管理, 复用 简略 nginx 中的 slab 的流程 # slab relatedvoid* poolPtr malloc(2048);ngx_slab_pool_t *pool (ngx_slab_pool_t *)poo…

Pytorch深度学习——线性回归实现 04(未完)

文章目录1 问题假设2 步骤3 学习使用Pytorch的API来搭建模型3.1 nn.Model3.2 优化器类3.3 评估模式和训练模式3.4 使用GPUdata和item的区别1 问题假设 假设我们的基础模型就是y wxb&#xff0c;其中w和b均为参数&#xff0c;我们使用y 3x0.8来构造数据x、y,所以最后通过模型…

0.django部署(基础知识)

我们前面的代码都是在我们自己的电脑&#xff08;通常是Windows操作系统&#xff09;上面运行的&#xff0c;因为我们还处于开发过程中。 当我们完成一个阶段的开发任务后&#xff0c;就需要把我们开发的网站服务&#xff0c;给真正的用户使用了。 那就需要我们的 网站 部署在…

【二次分配问题】基于遗传算法 (GA)、粒子群优化 (PSO) 和萤火虫算法 (FA) 求解二次分配( QAP)问题(MATLAB 实现)

目录 1 概述 3 Matlab代码及文章阅读 4 运行结果 4.1 萤火虫算法 4.2 粒子群优化算法 4.3 遗传算法 5 参考文献 1 概述 目前&#xff0c;该问题已经得到深入的研究&#xff0c;进化策略(evolutionstrategies)、遗传算法(genetic algorithms)、遗传规划(geneticprogramm…

警惕利用「以太坊合并」的 3 种骗局

原文作者&#xff1a;茉莉 距离以太坊合并还有不到 6 小时&#xff0c;这条被视作下一代互联网 Web3.0 底层基础设施的区块链网络将彻底改变共识机制&#xff0c;从工作量证明的 PoW 机制转向权益证明的 PoS。 在合并即将到来前&#xff0c;去中心化安全网络市场 PolySwarm 创…

各语言转wasm-js调用

起源是 我司应该是抄袭某家player , 也用wasm做的 , 所以我也研究一下 关于标题 我估计需要大家一起完善了 , 我只会讲一下 go c 别的都不会 webassembly( wasm ) 可以编译的如图 我想起我这边应用啊 也就无非播放器~~ 本地文件压缩啊加密啊或直接就上传了, 或者在操作数据…

RestHighLevelClient创建索引时报错[299 Elasticsearch-7.12.1

RestHighLevelClient创建索引时报错[299 Elasticsearch-7.12.1出现原因 : 这是因为在使用create方法时 , 会有两个选择 , 其中一个已经过时了 client.indices().create(request, RequestOptions.DEFAULT); 其中的create方法 , 有两个版本 , 有一个显示已经过时了 , 两个方法虽然…

蜂蜜什么时候喝,才可以获得蜂蜜更大的好处?真可以治疗咳嗽?

中秋节刚过去不久&#xff0c;家里面的礼品多的是不是可以开超市了?中国人讲究一个“礼”字&#xff0c;逢年过节、探望故友病友手里不带点东西就会难受。中秋节这样带有美好祝愿的节日自然也是中国人送礼的最佳时间之一。 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过…

Google Chrome Privacy Sandbox All In One

Google Chrome Privacy Sandbox All In OneGoogle Chrome Privacy Sandbox All In OneGoogle Chrome 隐私沙盒chrome://settings/privacySandbox With Privacy Sandbox trials, sites can deliver the same browsing experience using less of your info. That means more priv…

需要在html中加CSS,怎么加

在html中加CSS有三种方式 一种是直接写到标签上的style属性里面 <divid"mydiV"style"width:200px;border:1pxsolid#f00;margin:0;"></div> 一种是写到head标签里面的style标签里面 <styletype"text/css"> #mydiV{ width:2…

C++ 01 内存模型

内存分区的示意图。一般内存主要分为&#xff1a;代码区、常量区、静态区&#xff08;全局区&#xff09;、堆区、栈区这几个区域。 什么是代码区、常量区、静态区&#xff08;全局区&#xff09;、堆区、栈区&#xff1f; 代码区&#xff1a;存放程序的代码&#xff0c;即CPU执…

springboot 整合dubbo3开发rest应用

一、前言 作为微服务治理生态体系内的重要框架 dubbo&#xff0c;从出身到现在历经了十多年的市场检验而依旧火热&#xff0c;除了其自身优秀的设计&#xff0c;高性能的RPC性能&#xff0c;以及依托于springcloud-alibaba的这个背后强劲的开源团队支撑&#xff0c;在众多的微…

MongoDB6安装配置详解

官网下载地址&#xff1a; https://www.mongodb.com/try/download/community?tckdocs_server 打开后是这样的&#xff1a; 鼠标滑到上图红色箭头位置&#xff0c;可以看到最新版本目前是6.0.1&#xff0c;点击download下载即可&#xff0c;这里下载的是Windows版本。 下载好后…

vue插槽---作用域插槽(三)

编译作用域:模板中的变量,在模板对应的实例中查找相应的变量和数据。通俗的说就是父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。 作用域插槽:带参数的插槽,子组件提供给父组件参数,父组件决定其展示形式替换插槽标签。 为什…

哈希原理及模拟实现并封装unordered系列关联式容器

目录一、哈希1. 哈希概念2. 哈希冲突3. 哈希函数4. 哈希冲突的解决闭散列线性探测二次探测开散列开散列与闭散列比较二、哈希表哈希表的实现三、封装unordered系列关联式容器1. 封装unordered_set2. 封装unordered_map四、哈希表的应用1. 位图概念2. 应用3. 位图的实现2. 布隆过…

springboot客户关系管理系统源码 CRM小程序源码

CRM客户关系管理系统源码 crm小程序源码 基于springbootvue MySQL数据库开发的客户关系管理系统。 客户全流程高效管理&#xff0c;客户资料管理&#xff0c;客户跟踪管理&#xff0c;订单、合同管理&#xff0c;回款及交付管理等功能。 功能介绍 1、系统管理&#xff1a;员工…

基于STM32单片机和AD9850的智能DDS函数信号发生器

CSDN话题挑战赛第2期 参赛话题&#xff1a;学习笔记 文章目录1、整体设计2、硬件方案3、软件程序4、实物验证1、整体设计 有一天&#xff0c;我在浏览CSDN时看到一篇关于 AD9850 的帖子。AD9850是一款可以产生1hz到40mhz左右正弦波的芯片。淘宝的产品经销商能够将芯片与提供 T…

第二章-使用KNN和GBDT进行收入的预测分析

本文是《从零开始学python数据分析与挖掘》的第二章学习心得&#xff0c;相关数据可以从对应的官方数据库获取。 提供给你的只有一份收入相关的xlsx&#xff0c;你需要通过里面的数据进行年收入的预测。 1.数据预处理 首先读取数据&#xff0c;查看是否存在缺失值。对于存在…

关于模糊理论及简单应用

关于模糊理论及简单应用 1.开始 最近导师让我了解一下模糊理论,思考能不能结合现有技术实现创新点.这篇博客主要记录一下这两天对模糊理论的学习,以及做的一个小demo,希望如果有研究相关方面的大佬能留言相互交流学习. 之前用模糊c均值聚类的时候了解过scikit-fuzzy,这次发现…