Java开发相对于C语言最方便的点,就是代码上不需要主动去管理内存的回收,而由JVM负责分配回收。
标记出所有需要回收的内存对象,当垃圾回收时只清除标记的对象。缺点:回收的空间不连续,可能导致创建对象虽然未使用空间足够,但连续的空间不足无法创建。
将空间分为A、B两块。对象在A块创建。当A空间满了,垃圾回收,将A中存活的对象复制到B中,清空A空间。之后在B空间中尽享相同的逻辑。优点:合理规划了空间的连续性,每次回收都是整块内存的回收。缺点:将内存分为了两块,每次只能用一半的内存。
结合标记清除和标记复制的缺点,为了优化算法。提出了标记整理算法。对象的回收依然使用标记方式,在垃圾回收时,把存活的对象整理到内存一端,将其他内存回收。这样做的的好处时腾出了一整块连续的空间。优点:创造了连续的空间环境缺点:效率有所损失
根据对象的存活特性,将内存分为几个区域,分别对不同区域进行不同的回收算法。目前大部分的JVM都采用分代收集算法。常见的将堆分为年轻代、老年代,以及堆外的永久代。老年代存放的对象一般回收较少,年轻代回收的对象相对更多。一般对象多数是”朝生夕死“,即创建完很快就会被回收。针对这些区域的对象特性采用不同的收集算法,提高内存利用率。例如:以hotspot为例,内存划分为年轻代、老年代、永久代,年轻代又分为Eden、From Survivor、To Survivor
年轻代采用标记复制算法,对象的创建在Eden区进行,当Eden区进行垃圾回收时,将Eden存活对象复制到From Survivor区,清空Eden。当From Survivor也满了,将Eden区和From Survivor存活的对象复制到To Survivor区,清空Eden和From Survivor。老年代采用标记整理算法,将存活的对象整理到一端,腾出连续的空间。
Serial收集器是单线程收集器,是分代收集器。年轻代使用单线程复制收集算法(Serial收集器),老年代使用单线程标记整理算法(Serial Old收集器)。进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop TheWorld)。参数:-XX:UseSerialGC特点:单线程没有多线程切换的开销。
相当于Serial收集器的多线程版本。年轻代使用并行复制收集算法(ParNew 收集器),老年代使用单线程标记整理算法(Serial Old收集器)。参数:-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器;-XX:+UseParNewGC":强制指定使用ParNew;-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;特点:ParNew收集器在单CPU环境中不比Serial效果好,甚至可能更差,两个CPU也不一定跑的过,但随着CPU数量的增加,性能会逐步增加。
JDK8默认收集器。同样是并发收集器,Parallel Scavenge收集器的关注点与其他收集器不同, Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))Parallel Old是Parallel老年代收集器,使用标记整理算法。jdk1.6之前老年代只能使用Serial Old收集器。参数:
-XX:MaxGCPauseMillis
:控制最大垃圾收集停顿时间 -XX:GCTimeRatio
:设置吞吐量大小 -XX:+UseAdaptiveSizePolicy
:GC自适应的调节策略(GC Ergonomics),当这个参数打开之后,无需手动指定年轻代空间大小,也不需要设置Survivor对象年龄,虚拟机会根据运行情况,动态调整这些参数值。 CMS是老年代垃圾收集器,在收集过程中可以与用户线程并发操作。它可以与Serial收集器和Parallel New收集器搭配使用。CMS牺牲了系统的吞吐量来追求收集速度,适合追求垃圾收集速度的服务器上。参数:-XX:+UseConcMarkSweepGC来开启CMS。特点:并发收集、降低停顿时间
CMS收集器GC的过程
G1收集器的设计将整个堆内存分为多个大小相等的块(Region)。任然沿用年轻代、老年代的概念,但相对之前的空间连续,换成了多个Region的集合。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。G1收集器GC的过程
JDK11提供的新GC收集器,暂时没了解。
本文由博客一文多发平台 OpenWrite 发布!