JVM学习02-GC算法与种类
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 总结
- 首先是引用计数法,这个缺陷比較明显。JVM也没有採用。不做讨论。
- 标记清除和标记压缩相比。前者产生大量不连续的内存碎片。
后者尽管不产内存碎片,可是在前者的基础上还进行对象的压缩(整理),所以成本相对较高。
且两者都会进行标记和清除。效率不是非常高。
- 复制算法尽管比較简单高效。且不会产生内存碎片。可是明显浪费了大量的内存空间
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算法与种类的更多相关文章
- JVM学习02:GC垃圾回收和内存分配
JVM学习02:GC垃圾回收和内存分配 写在前面:本系列分享主要参考资料是 周志明老师的<深入理解Java虚拟机>第二版. GC垃圾回收和内存分配知识要点Xmind梳理 案例分析1-(G ...
- JVM学习笔记——GC算法
GC 算法 GC 即 Garbage Collection 垃圾回收.JVM 中的 GC 99%发生在堆中,而 Java 堆中采用的垃圾回收机制为分代收集算法.即将堆分为新生代和老年代,根据不同的区域 ...
- 【JVM虚拟机】(2)---GC 算法与种类
GC 算法与种类 对于垃圾收集(GC), 我们需要考虑三件事情:哪些内存需要回收?如何判断是垃圾对象?垃圾回收算法有哪些? 一.GC的工作区域 1.不是GC的工作区域 (1)程序计数器.虚拟机栈和本地 ...
- Java虚拟机JVM学习02 类的加载概述
Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...
- JVM中的GC算法,JVM参数,垃圾收集器分类
一.在JVM中什么是垃圾?如何判断一个对象是否可被回收?哪些对象可以作为GC Roots的根 垃圾就是在内存中已经不再被使用到的空间就是垃圾. 1.引用计数法: 内部使用一个计数器,当有对象被引用+1 ...
- JVM学习九:JVM之GC算法和种类
我们前面说到了JVM的常用的配置参数,其中就涉及了GC相关的知识,趁热打铁,我们今天就学习下GC的算法有哪些,种类又有哪些,让我们进一步的认识GC这个神奇的东西,帮助我们解决了C 一直挺头疼的内存回收 ...
- JVM学习二:JVM之GC算法和种类
我们前面说到了JVM的常用的配置参数,其中就涉及了GC相关的知识,趁热打铁,我们今天就学习下GC的算法有哪些,种类又有哪些,让我们进一步的认识GC这个神奇的东西,帮助我们解决了C 一直挺头疼的内存回收 ...
- JVM内核-原理、诊断与优化学习笔记(四):GC算法与种类
文章目录 GC的概念 GC算法 引用计数法 引用计数法的问题 标记清除 标记压缩 小问题 复制算法 复制算法的最大问题是:空间浪费 整合标记清理思想 -XX:+PrintGCDetails的输出 gc ...
- JVM学习之GC常用算法
出处:博客园左潇龙的技术博客--http://www.cnblogs.com/zuoxiaolong,多谢分享 GC策略解决了哪些问题? 既然是要进行自动GC,那必然会有相应的策略,而这些策略解决了哪 ...
- 深入JVM内核--GC算法和种类
GC的概念 Garbage Collection 垃圾收集 1960年 List 使用了GC Java中,GC的对象是堆空间和永久区 引用计数法 老牌垃圾回收算法 通过引用计算来回收垃圾 使用者 CO ...
随机推荐
- pat 甲级 1098. Insertion or Heap Sort (25)
1098. Insertion or Heap Sort (25) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yu ...
- jquery取得iframe中元素的方法
原文发布时间为:2010-12-27 -- 来源于本人的百度文章 [由搬家工具导入] 收集利用Jquery取得iframe中元素的几种方法 :contents() $.trim($("if ...
- Accelerating Enum-Based Dictionaries with Generic EnumComparer
原文发布时间为:2011-03-03 -- 来源于本人的百度文章 [由搬家工具导入] 文章:http://www.codeproject.com/KB/cs/EnumComparer.aspx 源码: ...
- 调用Outlook发送邮件
#region 查找与指定文件关联在一起的程序的文件名 /// <summary> /// 查找与指定文件关联在一起的程序的文件名 /// </summary> /// < ...
- compensation 在 spec 上的意義
compensation 翻譯為補償, 之前觀念都認為補償都是正的, 原來補償也可以負的, 例子: 電池充電電流在 10 - 45 度為 1800 mA, 在 0 - 10 度時,jeita 補償 1 ...
- linux(debian)安装USB无线网卡(tp-link TL-WN725N rtl8188eu )
1: 台式机家里面不想再走线了. 于是去某东买了个USB无线网卡.tp的WN725N USB,非常小, 和罗技的优联接收器差不多大. 2: 驱动能自己识别是不指望了,既然是usb网卡,插入USB后 ...
- hdu 4517(递推枚举统计)
小小明系列故事——游戏的烦恼 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)To ...
- 使用dvajs+webpack构建react开发环境
之前我有写过博文介绍过dva.js及其用法,dva.js固然是个非常优秀的框架,但是如果用dev-cli来创建的话就会默认绑定使用roadhog而不是webpack.鉴于roadhog的文档要明显少于 ...
- python第三方库安装-多种方式
第一种方式:安装whl文件 pip install whatever.whl 第二种方式:安装tar.gz文件 一般是先解压,然后进入目录之后,有setup.py文件 通过命令 python se ...
- Docker镜像原理和最佳实践
https://yq.aliyun.com/articles/68477 https://yq.aliyun.com/articles/57126 DockerCon 2016 深度解读: Dock ...