1.垃圾回收的意义

在java体系中内存的分和回收是自动化管理的,从而程序员不需要操心内存的分配和是否浪费。javaGC机制能帮我们把不需要的对象占用的内存回收,保证程序高效的运行。由于我们创建的对象都是放在JVM堆中,所以主要是对堆中的内存进行回收。

2.垃圾回收使用是算法

JVM在发生GC时会使用一定算法判断哪些对象是需要回收,即JVM垃圾的检测方式。那么一般采用的算法是引用计数法和根搜索法。

引用计数法:这个算法的实现是,给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。任何时刻计数值为0的对象就是不可能再被使用的。这种算法使用场景很多,但是,Java中却没有使用这种算法,因为这种算法很难解决对象之间相互引用的情况。看一段代码:

/*** 虚拟机参数:-verbose:gc*/
public class ReferenceCountingGC
{private Object instance = null;private static final int _1MB = 1024 * 1024;/** 这个成员属性唯一的作用就是占用一点内存 */private byte[] bigSize = new byte[2 * _1MB];public static void main(String[] args){ReferenceCountingGC objectA = new ReferenceCountingGC();ReferenceCountingGC objectB = new ReferenceCountingGC();objectA.instance = objectB;objectB.instance = objectA;objectA = null;objectB = null;System.gc();}
}

根搜索算法:

根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

java中可作为GC Root的对象有

 1.虚拟机栈中引用的对象(本地变量表)

 2.方法区中静态属性引用的对象

 3. 方法区中常量引用的对象

 4.本地方法栈中引用的对象(Native对象)

对于根搜索算法而言,未到达的对象并非是“非死不可”的,若要宣判一个对象死亡,至少需要经历两次标记阶段。如果对象在进行可达性分析后发现没有与GCRoots相连的引用链,则该对象被第一次标记并进行一次筛选,筛选条件为是否有必要的执行该对象的finalize方法,若对象没有覆盖finalize方法或者该finalize方法是否已经被虚拟机执行过了,则均视为不必要执行该对象的finalize方法,即该对象将会被回收。反之,若对象覆盖了finalize方法,并且该finalize方法并没有被执行过,那么,这个对象会被放置在一个叫做F-Queue的队列中,之后会由虚拟机自动建立的、优先级低的Finalizer线程去执行,而虚拟机不必要等待该线程执行结束,即虚拟机只负责建立线程,其他的事情交给线程去处理。

对F-Queue中对象进行第二次标记,如果对象在finalize方法中拯救了自己,即关联上了GCRoots引用链,,如把this关键字赋值给其他变量,那么在第二次标记的时候该对象将从“即将回收”的集合中移除,如果对象还是没有拯救自己,那就会被回收。
如下代码演示了一个对象如何在finalize方法中拯救了自己,然而,它只能拯救自己一次,第二次就被回收了。具体代码如下:

/**
* 此代码演示了两点:
* 1.此对象可以再被GC时自我拯救
* 2.这种自救的机会只有一次,因为一个对象的finalize方法最多只会被系统自动调用一次
*/
public class FinalizeEscapeGC {public String name;public static FinalizeEscapeGC SAVE_HOOK = null;public FinalizeEscapeGC(String name){this.name = name;}public void isAlive(){System.out.println("yes,i am still alive :)");}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("finalize method executed!");System.out.println(this);FinalizeEscapeGC.SAVE_HOOK = this;}@Overridepublic String toString() {return name;}public static void main(String[] args) throws InterruptedException {SAVE_HOOK = new FinalizeEscapeGC("leesf");System.out.println(SAVE_HOOK);//对象第一次拯救自己(第一次标记)SAVE_HOOK = null;System.out.println(SAVE_HOOK);System.gc();//第一次回收//因为finalize方法优先级很低,所以暂停0.5秒等待Thread.sleep(500);//此处调用了finalize方法,对象拯救了自己,this引用赋值if (SAVE_HOOK != null) {//此处判断非空,对象还处于存活状态SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}//下面这段代码与上面的完全相同,这是这一次却自救失败//一个对象的finalize方法只会被调用一次SAVE_HOOK = null;System.gc();//因为finalize方法优先级很低,所以暂停0.5秒等待Thread.sleep(500);//此处并没有调用finalize方法,因为finalize方法只会执行一次if (SAVE_HOOK != null) {//此处判断为nullSAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}}
}

运行结果如下:

由结果可知,该对象拯救了自己一次,第二次没有拯救成功,因为对象的finalize方法最多被虚拟机调用一次。

除了使用这两种算法判断哪些对象需要回收外,还可以通过对象的引用状态来识别是否需要做回收。有如下四种引用:

  1. 强引用
    代码中普遍存在的类似“Object obj = new Object()”这类的引用, 只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
  2. 软引用
    描述有些还有用但并非必须的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。Java中的类SoftReference表示软引用。
  3. 弱引用
    描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。Java中的类WeakReference表示弱引用。
  4. 虚引用
    这个引用存在的唯一目的就是在这个对象被收集器回收时收到一个系统通知,被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用

3、垃圾收集算法(方式)

1. 标记-清除(Mark-Sweep)算法(方式)
这是最基本的算法,标记-清除算法就如同它的名字一样,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。这种算法的不足主要体现在效率和空间,从效率的角度讲,标记和清除两个过程的的效率都不高;从空间的角度讲,标记清楚后会产生大量不连续的内存碎片,内存碎片太多可能会导致以后程序运行过程中在需要分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾回收动作。标记清除算法执行过程如图:

2. 复制(coping)算法(方式)
复制算法是为了解决效率问题而出现的,它将可用的内存分为两块,每次只用其中的一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存一次性清理掉。这样每次只需要对整个半区进行内存回收,内存分配的执行过程如图:

不过这种算法有个缺点,内存缩小为原来的一半,这样代价太高了。现在的商用模拟机都采用这种算法来回收新生代,不过研究表明1:1的比例非常不科学,因此新生代的内存被划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。每次回收时,将Eden和Survivor。每次回收时,将Eden和Survivor中还存活着的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden和刚才和刚才使用过的Survivor空间。HotSpot虚拟机默认Eden区和Survivor区的比例为8:1,意思是每次新生代中可用内存空间为整个新生代容量的90%。当然,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖老年代进行分配担保(Handle Promotion)。

3. 标记-整理(Mark-Compact)算法(方式)
复制算法在对象存活率较高的场景下要进行大量的复制操作,效率很低。万一对象100%存活,那么需要额外的空间进行分配担保。老年代都是不易被回收的对象,对象存活率高,因此一般不能直接选用复制算法。根据老年代的特点,有人提出了另外一种标记-整理算法,过程v与标记-清除算法一样,不过不是直接对可回收对象进行整理,而是让所有存活对象都向一端移动,然后清理掉边界以外的内存。标记-整理算法的工作过程如图:

4. 分代收集算法(方式)
根据上面的内容,用一张纸概括一下堆内存的布局。

现代商用虚拟机基本都采用分代收集算法来进行垃圾回收。这种算法结合了以上的内容,根据对象的生命周期的不同将内存划分为几块,然后根据各块的特点采用最适当的收集算法。大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担当的(老年代),采用标记-清理算法或者标记-整理算法。

4、垃圾收集器

垃圾收集器就是上面讲的理论知识的具体体现了。不同虚拟机所提供的垃圾收集器可能会有很大差别,我们使用的是HotSpot,HotSpot这个虚拟机所包含的所有收集器如图:

上图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,那说明他们可以搭配使用。虚拟机所处的区域说明它是属于新生代收集器还是老年代收集器,只能选择对具体应用最合适的收集器。这也是HotSpot为什么要实现这么多收集器的原因。

1. Serial收集器
最基本、发展历史最久的收集器,这个收集器采用复制算法的单线程的收集器,单线程一方面意味着他只会使用一个CPU或者一条线程去完成垃圾收集工作,另一方面也意味着他进行垃圾收集时必须暂停其他线程的所有工作,直到它收集结束为止。不过实际上到目前为止,Serial收集器依然是虚拟机运行在Client模式下的默认新生代收集器,因为它简单而高效。

2. Parnew收集器
Parnew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为和Serial收集器完全一样,但是他却是Server模式下的虚拟机首选的新生代收集器。除了Serial收集器外,目前只有它能与CMS收集器配合工作。CMS收集器第一次实现了让垃圾收集器与用户线程基本上同时工作。Parnew收集器默认开启的收集线程数与CPU数量相同,在CPU数量非常多的情况下,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。Parnew收集器运行过程如下图所示:

3. Parallel Scavenge收集器
Parallel Scavenge收集器也是一个新生代收集器,也采用了复制算法,也是并行的多线程收集器。CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是CPU用于运行用户代码时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。Parallel Scavenge收集器是虚拟机运行在Server模式下的默认垃圾收集器。

虚拟机提供了-XX:MaxGCPauseMills和-XX:G从TimeRatio两个参数来精确控制最大垃圾收集停顿时间和吞吐量大小。不过不要以为前者越小越好,GC停顿时间的缩短是以牺牲吞吐量和新生代空间换取的。由于与吞吐量关系密切,Parallel Scavenge收集器也被称为“吞吐量优先收集器”。

Parallel Scavenge收集器有一个参数-XX:UseAdaptiveSizePolicy参数,这是一个开关参数,这个参数打开之后,就不需要手动指定新生代大小、Eden区和Survivor参数等细节参数了,虚拟机会根据当前系统的运行情况以及性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。如果对于垃圾收集器运作原理不太了解,以至于在优化比较困难的时候,可以使用 Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成。

4. Serial Old收集器
Serial收集器的老年代版本,同样是一个单线程收集器,使用== “标记-整理算法”,这个收集器的主要意义也是在于给Client模式==下的虚拟机使用。

5. Parallel Old收集器
Parallel Scavenge收集器的老年代版本,使用多线程和== “标记-整理算法”==。这个收集器在JDK 1.6之后的出现,“吞吐量优先收集器”终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge收集器+Parallel Old收集器的组合。运行过程如下图所示:

6. CMS收集器

CMS(Conrrurent Mark Sweep,连续标记扫描)收集器是以获取最短回收停顿时间为目标的收集器。使用标记-清除算法,收集过程分为如下四步:
(1)初始标记,标记GCRoots能直接关联到的对象,时间很短。
(2)并发标记,进行GCRoots Tracing(可达性分析)过程,时间很长。
(3)重新标记,修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,时间较长。
(4)并发清除,回收内存空间,时间很长。

其中,并发标记与并发清除两个阶段耗时最长,但是可以与用户线程并发执行。运行过程如下图所示:

7. G1收集器
G1是目前技术发展的最前沿成果之一,HotSpot开发团队赋予它的使命是未来可以替换掉JDK1.5中发布的CMS收集器。与其他GC收集器相比,G1收集器具有以下特点:
(1)并发和并行。使用多个CPU来缩短Stop The World停顿时间,与用户线程并发执行。
(2)分代收集。独立管理整个堆,但是能够采用不同的方式去处理新创建对象和已经存活了一段时间、熬过多次GC的旧对象,以获取更好的收集效果。
(3)空间整合。基于标记-整理算法,无内存碎片产生。
(4)可预测的停顿。能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不得超过N毫秒。

在G1之前的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分(可以不连续)Region的集合。

5、Java有了GC同样会出现内存泄露问题

1.静态集合类像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象Object也不能被释放,因为他们也将一直被Vector等应用着。

Static Vector v = new Vector();
for (int i = 1; i<100; i++)
{ Object o = new Object(); v.add(o); o = null;
}

在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。

2.各种连接,数据库连接,网络连接,IO连接等没有显示调用close关闭,不被GC回收导致内存泄露。

3.监听器的使用,在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露。

不同垃圾收集器设置方式

XX:+UseSerialGC
启⽤串⾏收集器
-XX:+UseParallelGC
启⽤并⾏垃圾收集器,配置了该选项,那么-XX:+UseParallelOldGC默认 启⽤
-XX:+UseParNewGC
年轻代采⽤并⾏收集器,如果设置了-XX:+UseConcMarkSweepGC选 项,⾃动启⽤
-XX:ParallelGCThreads
年轻代及⽼年代垃圾回收使⽤的线程数。默认值依赖于 JVM 使⽤的 CPU 个

-XX:+UseConcMarkSweepGC ( CMS )
对于⽼年代,启⽤ CMS 垃圾收集器。 当并⾏收集器⽆法满⾜应⽤的延迟需 求是,推荐使⽤CMS 或 G1 收集器。启⽤该选项后,
-XX:+UseParNewGC ⾃动启⽤
-XX:+UseG1GC
启⽤ G1 收集器。 G1 是服务器类型的收集器, ⽤于多核、⼤内存的机器。 它在保持⾼吞吐量的情况下,⾼概率满⾜GC 暂停时间的⽬标

CMS,G1 垃圾回收器中的三色标记
三色标记法是一种垃圾回收法,它可以让 JVM 不发生或仅短时间发生 STW(Stop The World),从而达到清除 JVM 内存垃圾的目的。三色标记法将对象的颜色分为了黑、灰、白,三种颜色。

黑色:该对象已经被标记过了,且该对象下的属性也全部都被标记过了。(程序所需要的对 象);

灰色:对象已经被垃圾收集器扫描过了,但是对象中还存在没有扫描的引用(GC 需要从此对象中去寻找垃圾); 
白色:表示对象没有被垃圾收集器访问过,即表示不可达。 
CMS 解决办法:增量更新在应对漏标问题时,CMS 使用了增量更新(Increment Update)方法来做,在一个未被标记的对象(白色对象)被重新引用后,引用它的对象若为黑色则要变成灰色,在下次二次标记时让 GC 线程继续标记它的属性对象(但还是存在漏标的问题)。

CMS 另两个致命缺陷 CMS 采用了 Mark-Sweep 算法,最后会产生许多内存碎片,当到一定数量时,CMS 无法 清理这些碎片了,CMS 会让 Serial Old 垃圾处理器来清理这些垃圾碎片,而 Serial Old 垃圾处理器是单线程操作进行清理垃圾的,效率很低。 所以使用 CMS 就会出现一种情况,硬件升级了,却越来越卡顿,其原因就是因为进行Serial Old GC 时,效率过低。

解决方案:使用 Mark-Sweep-Compact 算法,减少垃圾碎片 调优参数(配套使用):-XX:+UseCMSCompactAtFullCollection 开启 CMS 的压缩 -XX:CMSFullGCsBeforeCompaction 默认为 0,指经过多少次 CMS FullGC 才进行压缩当 JVM 认为内存不够,再使用 CMS 进行并发清理内存可能会发生 OOM 的问题,而不得不进行 Serial Old GC,Serial Old 是单线程垃圾回收,效率低解决方案:降低触发 CMS GC 的阈值,让浮动垃圾不那么容易占满老年代调优参数: -XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让老年代占用率达到该值就进行 CMS GC 
G1 解决办法:SATB SATB(Snapshot At The Beginning), 在应对漏标问题时,G1 使用了 SATB 方法来做,具体流程:在开始标记的时候生成一个快照图标记存活对象在一个引用断开后,要将此引用推到 GC 的堆栈里,保证白色对象(垃圾)还能被 GC线程扫描到(在**write barrier(写屏障)**里把所有旧的引用所指向的对象都变成非白的)配合 Rset,去扫描哪些 Region 引用到当前的白色对象,若没有引用到当前对象,则回收G1 会不会进行 Full GC?会,当内存满了的时候就会进行 Full GC;且 JDK10 之前的 Full GC,为单线程的,所以 使用 G1 需要避免 Full GC 的产生。解决方案:加大内存;提高 CPU 性能,加快 GC 回收速度,而对象增加速度赶不上回收速度,则 Full GC 可 以避免;降低进行 Mixed GC 触发的阈值,让 Mixed GC 提早发生(默认 45%)

开启GC日志

一般来说,JDK8及以下版本通过以下参数来开启GC日志:

1 ‐XX:+PrintGCDetails

‐XX:+PrintGCDateStamps

‐Xloggc:gc.log

如果是在JDK9及以上的版本,则格式略有不同:

‐Xlog:gc*=info:file=

gc.log:time:filecount=0

Java8默认使用的垃圾收集器是什么?

Java8版本的Hotspot JVM,默认情况下使用的是并行垃圾收集器(Parallel GC)。其

他厂商提供的JDK8基本上也默认使用并行垃圾收集器。

Java9之后,官方JDK默认使用的垃圾收集器是G1。 。

总结:java垃圾回收机制首选是通过根搜索算法或者引用计数算法以及引用状态方式筛选出哪些对象是需要回收的。然后通过不同的回收方式或者几种不同方式组合进行会垃圾回收(分代收集算法是目前大部分JVM的垃圾收集器采用的收集方式(算法))。而垃圾收集器就是这些底层算法和方式的上层实现。其中G1垃圾收集器是当前最好的垃圾收集器。

java 垃圾回收机制的理解相关推荐

  1. java垃圾回收机制的理解

    Java垃圾回收机制算法 标记----清除算法 复制算法 标记----整理算法 分代收集算法 为什么要进行垃圾回收 因为当一个对象的引用不可达,或者一个对象没有任何引用指向它,那么它就没有必要在内存中 ...

  2. 深入理解 Java 垃圾回收机制

    转载自 http://www.cnblogs.com/andy-zcx/p/5522836.html 深入理解 Java 垃圾回收机制 一:垃圾回收机制的意义 java  语言中一个显著的特点就是引入 ...

  3. axis2 webService开发 附深入理解 Java 垃圾回收机制

    相关文章: axis web服务(axis2安装和eclipse.tomcat结合开发) axis2 webService开发 axis2 webService开发(打包aar文件) 使用Axis2开 ...

  4. 理解 Java 垃圾回收机制

    理解java垃圾回收机制有什么好处呢?作为一个软件工程师,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java应用程序. 这是我个人的主观观点,但我相信一个 ...

  5. 深入理解Java虚拟机:Java垃圾回收机制

    本篇内容包括:JAVA 垃圾回收机制概述.有哪些内存需要回收.如何回收(标记-清除.标记-整理(标记-清除-压缩).复制(标记-复制-清除).分代收集等算法) 以及 何时进行垃圾回收等内容! 一.概述 ...

  6. Java垃圾回收机制(Garbage Collection)

    引用博客地址:http://www.cnblogs.com/ywl925/p/3925637.html 以下两篇博客综合描述Java垃圾回收机制 第一篇:说的比较多,但是不详细 http://www. ...

  7. 36.JVM内存分哪几个区,每个区的作用是什么、如和判断一个对象是否存活、java垃圾回收机制、垃圾收集的方法有哪些、java类加载过程、类加载机制、双亲委派、Minor GC和Major GC

    36.JVM内存分哪几个区,每个区的作用是什么? 37.如和判断一个对象是否存活?(或者GC对象的判定方法) 38.简述java垃圾回收机制? 39.java中垃圾收集的方法有哪些? 40.java类 ...

  8. 【Java】Java垃圾回收机制

    Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给 ...

  9. Java垃圾回收机制总结

    Java垃圾回收机制 垃圾回收(GC,Garbage Collection)是 Java 虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象所占据的内存空间的一种机制.注 ...

最新文章

  1. 如何开启Dubbo框架内部的日志?
  2. Vue+ElemtUI中对el-dialog弹出框宽度调整方法
  3. 讲解虚拟服务器的书_程序员不得不看的书
  4. android getter不起作用,java - 如何在Android Studio中自动生成getter和setter
  5. 【Servlet】Cookie会话跟踪技术
  6. 游戏行业很火,游戏建模师好不好做呢?怎样的建模师公司才欢迎?
  7. IntelliJ IDEA这样配置,代码效率嗖嗖的
  8. Redis学习笔记(四) 发布订阅命令
  9. 张量/tensor--详解
  10. C均值聚类算法及其实现
  11. 1.线性回归、梯度下降法、岭回归、LASSO回归、最小二乘法
  12. 【中文分词】结构化感知器SP
  13. 税收学考试可以带计算机吗,税务师考试能带计算器和草纸吗?简答题需要用笔吗?...
  14. 小包实用工具:国家代码大全
  15. 新混响预设合集 – Audio Ease Altiverb 7 New Impulse Responses
  16. 看得更近,监督得更好:通过基于组件的鉴别器一次性生成字体
  17. Python绘制股票K线图
  18. 【每天一个java设计模式(完)】 - 四万字实现23种设计模式(附示例源码)
  19. frequentism-and-bayesianism-chs-ii
  20. 求一元二次方程的根 java

热门文章

  1. 职业培训之简历的写法
  2. mysql去掉哔哔音
  3. 示例:教你在.NET应用程序中创建OMR工作表扫描仪软件
  4. TCP拥塞控制算法(Tahoe/Reno/Newreno)
  5. 向量内积(点乘)和外积(叉乘)概念及几何意义【转】
  6. ajax同步请求 超时时间问题
  7. 关于数据库收缩的几点建议
  8. linux设置文件为不可访问权限,Centos给文件设置了777权限仍不能访问解决方案
  9. linux lftp下载目录,linux中使用lftp上传下载文件
  10. 洗车店小程序管理系统源码 含完整前后端+搭建教程