1. GC 简单介绍

  GC(Garbage Collection) 是垃圾收集的简写,GC机制是java中一个比較重要的概念。java的内存管理提供了内存的分配和释放。内存处理是程序编写人员非常easy出错的地方。忘记或错误的内存回收非常easy导致系统的不稳定,甚至瘫痪。

java的GC机制能够非常好的检測对象是否超过作用域而能够达到回收的要求,从而实现自己主动回收垃圾对象的释放内存的目的。

   事实上早在非常久曾经(1960)就已经有了GC的概念,仅仅是java借用这个优秀的思想,在java内存模型中。GC的工作区域主要是在堆区域和方法区。大部分都是在堆区域中。

2. GC 算法

2.1 引用计数法

  首先呢,java并没有採用引用计数法作为GC算法,因为它有明显的缺陷。

  引用计数法实现比較简单:对于一个对象A来说。仅仅要有不论什么一个对象或者引用指向了A。那么A的计数器就加1。当引用失效时。引用计数器就减1,最后仅仅要对象A的引用计数器的值为0,那么A就不可能再被使用,就可被视为垃圾。



  引用计数法存在明显的缺陷,第一因为不断引用和去除引用伴随着加减法。影响性能。第二最大是问题就是非常难解决循环引用的问题。



这样就会存在非常多的对象无法被回收,所以JVM压根没有使用引用计数法作为GC算法。

2.2 标记-清除

  标记清除(Mark-Sweep)算法是现代垃圾回收算法的基础。顾名思义。标记清除算法将垃圾清除分为两个阶段:标记和清除

标记:通过根节点。标记全部从根节点開始的可达对象。因此,未被标记的对象就是未被引用即不可达的对象。

清除:清除全部未被标记的对象。



2.3 标记-压缩

  标记压缩(Mark-Compact)算法适用于存活对象比較多的场合,比方老年代。它在标记-清除算法基础上做了一些优化,标记压缩算法和标记清除算法一样。首先通过根节点标记可达的对象。然后还将全部可达的对象(存活的对象)压缩到内存一端。然后清理边界外全部空间。

标记:通过根节点标记可达的对象。

压缩:将存活的对象整理到内存一端。清理边界外全部空间。

2.4 复制算法

与标记清除算法相比,复制(Copying)算法相对是一个比較高效的算法,因为涉及到存活对象的赋值,所以复制算法不适合存活对象比較多的场合(如不适合老年代)。复制算法的思想大致例如以下:将原有的内存分为两块空间,每次仅仅使用当中一块,在垃圾回收的时候,将正在使用内存中存活的对象拷贝到未使用的内存块中,之后清除正在使用内存中的全部对象,然后交换两个内存的角色完毕垃圾回收。

2.5 增量算法

另一种算法是增量算法(直接摘抄了):增量算法的基本思想是,假设一次性将全部的垃圾进行处理,须要造成系统长时间的停顿,那么就能够让垃圾收集线程和应用程序线程交替运行。每次。垃圾收集线程仅仅收集一小片区域的内存空间,接着切换到应用程序线程。

依次重复,直到垃圾收集完毕。使用这样的方式,因为在垃圾回收过程中,间断性地还运行了应用程序代码。所以能减少系统的停顿时间。可是,因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。

2.6 总结

  1. 首先是引用计数法,这个缺陷比較明显。JVM也没有採用。不做讨论。

  2. 标记清除和标记压缩相比。前者产生大量不连续的内存碎片。

    后者尽管不产内存碎片,可是在前者的基础上还进行对象的压缩(整理),所以成本相对较高。

    且两者都会进行标记和清除。效率不是非常高。

  3. 复制算法尽管比較简单高效。且不会产生内存碎片。可是明显浪费了大量的内存空间

3. 分代思想

  依据GC算法的总结能够知道,结合不同算法的有点才干规划出一套比較可行的方案,所谓没有最好仅仅有更好。

  前面JVM内存模型中有说到将JAVA堆分为新生代和老年代。然后又将新生代细分为eden space。survivor space(分为from和to或者s0和s1)。然后依据对象的存活周期将短命对象归为新生代,长命对象归为老年代。

  然后依据不同代的特点,在对象存活比較少的新生代採用复制算法。在老年代採用标记清除或标记压缩算法。



图中。在进行垃圾回收的时候。存活对象怎样存放有非常多种可能。

1. 左边绿色的箭头表示在垃圾回收的时候。第一种年龄比較大的对象会存放在老年代中;另外一种情况就是一些比較大的对象无法放到 survivor 空间中。那么此时大对象也被存放到老年代中,所以有的是也称老年代是一个担保空空。

2. 左边红色和黄色的箭头就是进行正常的复制算法。垃圾回收结束就是右图的样子。

3. 有的时候查看GC日志的时候,新生代的垃圾回收一般称为minor gc。老年代因为区域比較大且存活对象非常多。生命周期非常长,所以gc时候会比較长。通常称为full gc。

4. 对象的可触及性

对象的可触及性顾名思义就是从根节点能够标记的对象。大家普通情况会觉得对象要么是可触及的,要么是不可触及的,事实上中间还存在一个可复活性。

1. 可触及性

 从根节点能够触及的对象

2. 可复活性

一旦全部引用被释放。对象进入可复活状态,因为在 finalize() 方法后对象可能变得可触及

3. 不可触及性

finalize() 方法之后,对象可能进入不可触及状态。不可触及的对象不能复活。然后进入可回收的状态。

这里须要注意的一点是 finalize() 方法仅仅会被运行一次,所以某个对象不能无限又可复活性到达可触及性。

并且 finalize() 方法的运行优先级非常低,何时出发GC并不确定。finalize()方法的调用也变得不确定。

更具体參照:《finalize 总结》

5. Stop-The-World (STW) 现象

  STW现象是java的一种全局暂停的现象。全部Java代码停止运行(Native代码能够运行,但不能与JVM交互)。产生这样的现象的原因多半是因为 GC 引起的。

GC 为什么产生 STW 现象?这个以开 Party 打个比方:我们在开party 的时候会产生非常多的垃圾,那么此时假设有人来清理垃圾。我们他在清理的时候。我们又不断产生垃圾。那么房间永远打扫不干净,所以唯一的办法就是我们停止手中的事情,直到房间打扫干净后再进行活动。

  那么GC也是这个道理,当发生GC的时候,必定全部的工作线程会停止,那么此时就会产生java停顿的现象。

  当发生STW现象的时候。假设时间短还好,假设时间特别长甚至几十分钟,服务器就会长时间得不到响应,那么就会带来比較大的危害。解决方法能够使用主备机的切换吧。具体不是非常清楚,也不展开了。

6. 垃圾回收器的种类

6.1 新生代串行收集器

串行收集器是一个古老而稳定,经过长时间考验的垃圾收集器。

在诸如单 CPU 处理器或者较小的应用内存等硬件平台不是特别优越的场合,它的性能表现能够超过并行回收器和并发回收器。

可是有的时候会停顿非常长时间,且是线程独占的。

新生代串行收集器採用了复制算法,当 JVM 在 Client 模式下运行时。它是默认的垃圾收集器。

在 HotSpot 虚拟机中。使用 -XX:+UseSerialGC 參数能够指定使用新生代串行收集器和老年代串行收集器。此时老年代串行收集器採用的是标记-压缩算法

[GC 0.844: [DefNew: 17472K->2176K(19648K), 0.0188339 secs] 17472K->2375K(63360K), 0.0189186 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

6.2 老年代串行收集器

老年代串行收集器和新生代串行收集器一样。老年代串行收集器採用的是标记-压缩算法,能够使用 -XX:+UseSerialGC 參数能够指定使用新生代串行收集器和老年代串行收集器。

图解和新生代串行收集器一样。

 [Full GC 8.259: [Tenured: 43711K->40302K(43712K), 0.2960477 secs] 63350K->40302K(63360K), [Perm : 17836K->17836K(32768K)], 0.2961554 secs] [Times: user=0.28 sys=0.02, real=0.30 secs]

6.3 并行收集器

并行收集器工作在新生代,仅仅是将新生代串行收集器变成了多线程化了,它的回收策略、算法以及參数和串行回收器一样。

新生代依然採用复制算法,老年代还是串行收集器(标记压缩算法)。GC依然是线程独占的。

开启并行回收器能够使用參数 -XX:+UseParNewGC



多线程并不意味着GC一定会非常快,且须要多核CPU的支持才会相对提高效率。

能够使用 -XX:ParallelGCThreads 限制线程数量

[GC 0.834: [ParNew: 13184K->1600K(14784K), 0.0092203 secs] 13184K->1921K(63936K), 0.0093401 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

6.4 新生代并行回收收集器

与 ParNew 相似。新生代并行回收收集器也是使用复制算法的收集器。从表面上看,它和并行收集器一样都是多线程、独占式的收集器。

可是。并行回收收集器有一个重要的特点:它非常关注系统的吞吐量

使用 -XX:+UseParallelGC 设置 新生代使用Parallel收集器+ 老年代使用串行收集器

使用 -XX:+UseParallelOldGC 设置 新生代和老年代都使用Parallel收集器

[Full GC [PSYoungGen: 2682K->0K(19136K)] [ParOldGen: 28035K->30437K(43712K)] 30717K->30437K(62848K) [PSPermGen: 10943K->10928K(32768K)], 0.2902791 secs] [Times: user=1.44 sys=0.03, real=0.30 secs]

6.5 老年代并行回收收集器

老年代的并行收集器和新生代并行收集器一样。所线程且关注系统吞吐量。採用标记压缩的算法。

使用 -XX:+UseParallelOldGC 设置 新生代和老年代都使用Parallel收集器

图解和清单和新生代并行收集器一样。

这里新生代和老年代的并行收集器和使用下面參数启动:

-XX:MaxGCPauseMills : 设置最大的停顿时间,单位是毫秒。

GC会尽力保证回收的时间不超过吞吐量。

-XX:GCTimeRatio : 设置吞吐量的大小n。GC时间比[ 1/(1+n) ]。默认值为99。即最大同意1%的时间用于垃圾回收。

这两个參数本来就是矛盾的,假设将最大停顿时间设置越小,那么GC就会越频繁,从而减少整个系统的吞吐量。

假设吞吐量设置越大,GC导致的停顿时间也会越长,所以有所矛盾。所以在实际中也能够採用自适应的GC调节策略,使用 -XX:+UseAdaptiveSizePolicy 能够打开自适应 GC 策略。在这样的模式下。新生代的大小、eden 和 survivor 的比例、晋升老年代的对象年龄等參数会被自己主动调整。以达到在堆大小、吞吐量和停顿时间之间的平衡点。

在手工调优比較困难的场合,能够直接使用这样的自适应的方式,仅指定虚拟机的最大堆、目标的吞吐量 (GCTimeRatio) 和停顿时间 (MaxGCPauseMills),让虚拟机自己完毕调优工作。

6.6 CMS收集器

  CMS (Concurrent Mark Sweep)是并发标记清除的收集器,这里的并发表示能够和用户线程一起工作。且採用标记清除算法

  CMS 收集器针是老年代收集器,新生代採用 ParNew 并行收集器。

  能够使用 -XX:+UseConcMarkSweepGC 启动 CMS 收集器。

  CMS 工作大致可分为例如以下几个过程:

1. 初始标记:GC 线程独占,对根能够直接关联到的对象进行标记,速度比較快。

2. 并发标记:GC 线程和用户线程一起运行,对全部的对象进行标记。

3. 又一次标记:因为上一步并发过程。用户线程可能还会生产出垃圾,所以GC 线程独占。在正式清理前又一次做一次标记。

4. 并发清除:GC 线程和用户线程一起运行。GC 回收垃圾对象。

1.662: [GC [1 CMS-initial-mark: 28122K(49152K)] 29959K(63936K), 0.0046877 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.666: [CMS-concurrent-mark-start]
1.699: [CMS-concurrent-mark: 0.033/0.033 secs] [Times: user=0.25 sys=0.00, real=0.03 secs]
1.699: [CMS-concurrent-preclean-start]
1.700: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.700: [GC[YG occupancy: 1837 K (14784 K)]1.700: [Rescan (parallel) , 0.0009330 secs]1.701: [weak refs processing, 0.0000180 secs] [1 CMS-remark: 28122K(49152K)] 29959K(63936K), 0.0010248 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.702: [CMS-concurrent-sweep-start]
1.739: [CMS-concurrent-sweep: 0.035/0.037 secs] [Times: user=0.11 sys=0.02, real=0.05 secs]
1.739: [CMS-concurrent-reset-start]
1.741: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

CMS尽管是并发收集器,可是事实上也会存在线程独占的情况导致全局停顿,仅仅是减少了停顿的时间罢了。因为用户线程的运行过程中,GC的时候还须要分CPU去做垃圾回收,这样就会大大减少总体系统的反应速速。在清理阶段。因为和用户线程并发运行,还会产生新的垃圾,导致清理不彻底。甚至还可能会存在产生的垃圾使得CMS来不及清理。让可使用内存的容量迅速减小。直到内存预留不够,引起 concurrent mode failure 错误。

解决方式:

第一能够通过 -XX:CMSInitiatingOccupancyFraction 的值来设置触发CMS收集器的阀值。

默觉得68,即当老年代空间使用率达到68%的时候触发CMS回收。

第二当引起concurrent mode failure 错误的时候,JVM就会启动备用回收器 **老年代串行回收器** 作为GC回收器。

CMS收集器的一些參数:

1. -XX:+UseCMSCompactAtFullCollection 參数将在进行一个Full GC之后进行一次内存压缩(整理),因为CMS採用的是标记清除算法。因为整理过程是线程独占的。所以可能引起的停顿时间较长。

2. -XX:+CMSFullGCsBeforeCompaction 參数设置多少次Full GC后进行一次内存整理。

3. -XX:ParallelCMSThreads 參数设置CMS线程的数量。

6.7 G1收集器

G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同一时候,还具备高吞吐量性能特征. 在Oracle JDK 7 update 4 及以上版本号中得到全然支持, 专为下面应用程序设计:

1. 能够像CMS收集器一样,GC操作与应用的线程一起并发运行

2. 紧凑的空暇内存区间且没有非常长的GC停顿时间.

3. 须要可预測的GC暂停耗时.

4. 不想牺牲太多吞吐量性能.

5. 启动后不须要请求更大的Java堆.

G1的长期目标是代替CMS(Concurrent Mark-Sweep Collector, 并发标记-清除). 因为特性的不同使G1成为比CMS更好的解决方式. 一个差别是,G1是一款压缩型的收集器,G1通过有效的压缩全然避免了对细微空暇内存空间的分配,不用依赖于regions。这不仅大大简化了收集器。并且还消除了潜在的内存碎片问题。

除压缩以外,G1的垃圾收集停顿也比CMSeasy预计,也同意用户自己定义所希望的停顿參数(pause targets)。

具体參考博文:《G1垃圾收集器入门》

7. GC 相关的參数

1. 与串行回收器相关的參数

-XX:+UseSerialGC : 在新生代和老年代使用串行回收器。

-XX:+SuivivorRatio : 设置 eden 区大小和 survivor 区大小的比例。8表示 两个Survivor:eden=2:8。即一个Survivor占年轻代的1/10。

-XX:NewRatio : 新生代和老年代(不包括永久区)的比。4 表示 新生代:老年代=1:4,即年轻代占堆的1/5。

-XX:+PretenureSizeThreshold : 设置大对象直接进入老年代的阈值。

当对象的大小超过这个值时,将直接在老年代分配。

-XX:MaxTenuringThreshold : 设置对象进入老年代的年龄的最大值。

每一次 Minor GC 后,对象年龄就加 1。不论什么大于这个年龄的对象,一定会进入老年代。

2. 与并行 GC 相关的參数

-XX:+UseParNewGC : 在新生代使用并行收集器。

-XX:+UseParallelOldGC : 老年代使用并行回收收集器。

-XX:ParallelGCThreads:设置用于垃圾回收的线程数。通常情况下能够和 CPU 数量相等。但在 CPU 数量比較多的情况下,设置相对较小的数值也是合理的。

-XX:MaxGCPauseMills:设置最大垃圾收集停顿时间。它的值是一个大于 0 的整数。收集器在工作时,会调整 Java 堆大小或者其它一些參数,尽可能地把停顿时间控制在 MaxGCPauseMills 以内。

-XX:GCTimeRatio : 设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。

-XX:+UseAdaptiveSizePolicy : 打开自适应 GC 策略。

在这样的模式下,新生代的大小,eden 和 survivor 的比例、晋升老年代的对象年龄等參数会被自己主动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。

3. 与 CMS 回收器相关的參数

-XX:+UseConcMarkSweepGC : 新生代使用并行收集器,老年代使用 CMS + 串行收集器(备用收集器)。

-XX:+ParallelCMSThreads : 设定 CMS 的线程数量。

-XX:+CMSInitiatingOccupancyFraction : 设置 CMS 收集器在老年代空间被使用多少后触发,默觉得 68%。

-XX:+UseFullGCsBeforeCompaction : 设定进行多少次 CMS 垃圾回收后,进行一次内存压缩。

-XX:+CMSClassUnloadingEnabled : 同意对类元数据进行回收。

-XX:+CMSParallelRemarkEndable : 启用并行重标记。

-XX:CMSInitatingPermOccupancyFraction : 当永久区占用率达到这一百分比后。启动 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。

-XX:UseCMSInitatingOccupancyOnly : 表示仅仅在到达阈值的时候,才进行 CMS 回收。

-XX:+CMSIncrementalMode : 使用增量模式。比較适合单 CPU。

4. 与 G1 回收器相关的參数

-XX:+UseG1GC:使用 G1 回收器。

-XX:+UnlockExperimentalVMOptions : 同意使用实验性參数。

-XX:+MaxGCPauseMills : 设置最大垃圾收集停顿时间。

-XX:+GCPauseIntervalMills : 设置停顿间隔时间。

5. 其它參数

-XX:+PrintGCDetails : 打开显示GC日志的开关。

-XX:+DisableExplicitGC : 禁用显示 GC。

-Xloggc:Xxx.log : 设置GC的log位置和名称。

-XX:+HeapDumpOnOutOfMemoryError : 当堆内存移除出错的时候显示最后的GC日志。

-Xmx 和 –Xms : 设置堆内存的最大同意值和最小值,如-Xmx32M -Xms32M

-XX:PermSize 和 -XX:MaxPermSize : 设置永久代的最小初始值和最大同意值 如-XX:PermSize=64MB 和 -XX:MaxPermSize=256M。

-XX:MaxPermSize缺省值和client/server选项相关,-server选项下默认MaxPermSize为64m,-client选项下默认MaxPermSize为32m。

-Xmn : 设置新生代大小 , 如 -Xmn16M

JVM学习02-GC算法与种类的更多相关文章

  1. JVM学习02:GC垃圾回收和内存分配

    JVM学习02:GC垃圾回收和内存分配 写在前面:本系列分享主要参考资料是  周志明老师的<深入理解Java虚拟机>第二版. GC垃圾回收和内存分配知识要点Xmind梳理 案例分析1-(G ...

  2. JVM学习笔记——GC算法

    GC 算法 GC 即 Garbage Collection 垃圾回收.JVM 中的 GC 99%发生在堆中,而 Java 堆中采用的垃圾回收机制为分代收集算法.即将堆分为新生代和老年代,根据不同的区域 ...

  3. 【JVM虚拟机】(2)---GC 算法与种类

    GC 算法与种类 对于垃圾收集(GC), 我们需要考虑三件事情:哪些内存需要回收?如何判断是垃圾对象?垃圾回收算法有哪些? 一.GC的工作区域 1.不是GC的工作区域 (1)程序计数器.虚拟机栈和本地 ...

  4. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  5. JVM中的GC算法,JVM参数,垃圾收集器分类

    一.在JVM中什么是垃圾?如何判断一个对象是否可被回收?哪些对象可以作为GC Roots的根 垃圾就是在内存中已经不再被使用到的空间就是垃圾. 1.引用计数法: 内部使用一个计数器,当有对象被引用+1 ...

  6. JVM学习九:JVM之GC算法和种类

    我们前面说到了JVM的常用的配置参数,其中就涉及了GC相关的知识,趁热打铁,我们今天就学习下GC的算法有哪些,种类又有哪些,让我们进一步的认识GC这个神奇的东西,帮助我们解决了C 一直挺头疼的内存回收 ...

  7. JVM学习二:JVM之GC算法和种类

    我们前面说到了JVM的常用的配置参数,其中就涉及了GC相关的知识,趁热打铁,我们今天就学习下GC的算法有哪些,种类又有哪些,让我们进一步的认识GC这个神奇的东西,帮助我们解决了C 一直挺头疼的内存回收 ...

  8. JVM内核-原理、诊断与优化学习笔记(四):GC算法与种类

    文章目录 GC的概念 GC算法 引用计数法 引用计数法的问题 标记清除 标记压缩 小问题 复制算法 复制算法的最大问题是:空间浪费 整合标记清理思想 -XX:+PrintGCDetails的输出 gc ...

  9. JVM学习之GC常用算法

    出处:博客园左潇龙的技术博客--http://www.cnblogs.com/zuoxiaolong,多谢分享 GC策略解决了哪些问题? 既然是要进行自动GC,那必然会有相应的策略,而这些策略解决了哪 ...

  10. 深入JVM内核--GC算法和种类

    GC的概念 Garbage Collection 垃圾收集 1960年 List 使用了GC Java中,GC的对象是堆空间和永久区 引用计数法 老牌垃圾回收算法 通过引用计算来回收垃圾 使用者 CO ...

随机推荐

  1. pat 甲级 1098. Insertion or Heap Sort (25)

    1098. Insertion or Heap Sort (25) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yu ...

  2. jquery取得iframe中元素的方法

    原文发布时间为:2010-12-27 -- 来源于本人的百度文章 [由搬家工具导入] 收集利用Jquery取得iframe中元素的几种方法 :contents()  $.trim($("if ...

  3. Accelerating Enum-Based Dictionaries with Generic EnumComparer

    原文发布时间为:2011-03-03 -- 来源于本人的百度文章 [由搬家工具导入] 文章:http://www.codeproject.com/KB/cs/EnumComparer.aspx 源码: ...

  4. 调用Outlook发送邮件

    #region 查找与指定文件关联在一起的程序的文件名 /// <summary> /// 查找与指定文件关联在一起的程序的文件名 /// </summary> /// < ...

  5. compensation 在 spec 上的意義

    compensation 翻譯為補償, 之前觀念都認為補償都是正的, 原來補償也可以負的, 例子: 電池充電電流在 10 - 45 度為 1800 mA, 在 0 - 10 度時,jeita 補償 1 ...

  6. linux(debian)安装USB无线网卡(tp-link TL-WN725N rtl8188eu )

    1: 台式机家里面不想再走线了. 于是去某东买了个USB无线网卡.tp的WN725N  USB,非常小, 和罗技的优联接收器差不多大. 2:  驱动能自己识别是不指望了,既然是usb网卡,插入USB后 ...

  7. hdu 4517(递推枚举统计)

    小小明系列故事——游戏的烦恼 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)To ...

  8. 使用dvajs+webpack构建react开发环境

    之前我有写过博文介绍过dva.js及其用法,dva.js固然是个非常优秀的框架,但是如果用dev-cli来创建的话就会默认绑定使用roadhog而不是webpack.鉴于roadhog的文档要明显少于 ...

  9. python第三方库安装-多种方式

    第一种方式:安装whl文件 pip install whatever.whl   第二种方式:安装tar.gz文件 一般是先解压,然后进入目录之后,有setup.py文件 通过命令 python se ...

  10. Docker镜像原理和最佳实践

    https://yq.aliyun.com/articles/68477 https://yq.aliyun.com/articles/57126  DockerCon 2016 深度解读: Dock ...