java堆根据对象存活周期的长短分为几块:一般为新生代,老年代及永久代(对HotSpot而言)。
堆内存是虚拟机管理内存最大的区域,也是垃圾回收最频繁的一块区域,java所有的对象都放在堆当中,对堆内存进行分代是为了提高对对象的内存分配和垃圾回收的效率。如果没有分代,生命周期长的和短的对象都放在一起,随着程序的执行,堆内存进行者频繁的垃圾回收,而每次都要对对象进行遍历,所耗费的时间是巨大的,会对生命周期短的对象进行垃圾回收,堆内存就会产生”碎片“,若新创建的对象较大,而”碎片“较小,就不能将该对象放在”碎片“中,会造成内存的浪费
有了内存分代就不同了,新创建的对象会放在新生代中,进过多次垃圾回收任然活下来的会放在老年代中,静态属性,类信息存放在永久代中,新生代的对象生命周期短,只需要在新生代中频繁的进行GC即可,对于老年代中的生命周期长的对象则不需要频繁的GC,对于永久代则不进行GC,还可以根据不同代的特点采用不要、同的垃圾回收策略,大大提高性能
永久代是HotSpot独有概念,(JDK1.5之前没有HotSpot,JDK1.7开始区去掉永久代,将常量池放在方法区中),它主要存放类信息,静态变量,数字常量,新生代和老年代是垃圾回收的主要区域
新创建的对象放在新生区,新生区的对象大多生命周期很多,一次常规的GC可以回收70%-95%对象,回收率很高
HotPost将新生区分为三个部分,Eden,两个Survivor(from和to),空间比例默认为8:1:1,这样划分的目的是因为HotSpot采用复制清除算法来进行GC,可以充分利用空间,减少浪费
新创建的对象首先会被置于在Eden区(大对象直接放在老年区),当Eden区没有足够的空间进行分配时,虚拟机会发起一次Minor GC,当某一次GC开始时,只有Eden区和from区有对象,to区空白没有对象,当GC进行时,Eden区死掉的释放空间,存活下来的对象年龄加1并转移到to区同时清空Eden区,from区死掉的对象释放空间,存活下来的对象年龄加1,如果对象年龄=年龄阀值时,会被转移到老年区,如果年龄<年龄阀值(年龄阀值默认15,对象每经过一次GC,保存下来的年龄加1,年龄保存到对象的header),则会被复制(复制算法)到to区并清空from区,如果to区内存不足,则将对象转移到old区(作为担保),当GC结束时,Eden区和from没有对象,to区有对象;当下一次GC开始时,to区和from交换角色,做同样的事,
老年代中的对象来自新生代中年龄达到年龄阀值的对象或者被担保的对象,老年代中GC频率较低,回收率较低
存储类的信息,静态变量,常量等数据,一般不会进行垃圾回收
把内存空间分为两个相等的区域,每次只使用其中一个区域,GC时,存活下来的对象,复制到另一个区域,并整理对象的存储位置,不会出现”碎片“,同时清理当前使用区域。此算法的缺点是需要两倍存储空间。
此算法分为两个阶段,第一阶段从引用的根节点开始标记所有被引用的对象,第二阶段遍历整个堆,会将没有标记的对象清除,此算法的缺点是会暂停整个应用,并且会产生内存”碎片“
此算法结合了复制算法和标记清除算法的优点,也分两个阶段,只是没有内存”碎片“产生
java中的垃圾收集器是对垃圾回收算法的具体实现,
Scavenge GC:是发生在新生代的GC,使用速度快,效率高的算法,当Eden区内存紧张时会被触发;
Full GC:是发生在老年代的GC,当老年代内存级紧张时会触发全收集,也可以通过system.gc()来调用
Serial收集器是Hotspot运行在Client模式下的默认新生代收集器, 它的特点是:只用一个CPU(计算核心)/一条收集线程去完成GC工作, 且在进行垃圾收集时必须暂停其他所有的工作线程(“Stop The World” -后面简称STW)。可以使用-XX:+UseSerialGC打开。 虽然是单线程收集, 但它却简单而高效, 在VM管理内存不大的情况下(收集几十M~一两百M的新生代), 停顿时间完全可以控制在几十毫秒~一百多毫秒内。
安全点:STW的点
ParNew收集器其实是前面Serial的多线程版本, 除使用多条线程进行GC外, 包括Serial可用的所有控制参数、收集算法、STW、对象分配规则、回收策略等都与Serial完全一样(也是VM启用CMS收集器-XX: +UseConcMarkSweepGC的默认新生代收集器)。
由于存在线程切换的开销, ParNew在单CPU的环境中比不上Serial, 且在通过超线程技术实现的两个CPU的环境中也不能100%保证能超越Serial. 但随着可用的CPU数量的增加, 收集效率肯定也会大大增加(ParNew收集线程数与CPU的数量相同, 因此在CPU数量过大的环境中, 可用-XX:ParallelGCThreads=
与ParNew类似, Parallel Scavenge也是使用复制算法, 也是并行多线程收集器. 但与其他收集器关注尽可能缩短垃圾收集时间不同, Parallel Scavenge更关注系统吞吐量:
系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
停顿时间越短就越适用于用户交互的程序-良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务-可以最高效率地利用CPU时间,尽快地完成程序的运算任务. Parallel Scavenge提供了如下参数设置系统吞吐量
Serial Old是Serial收集器的老年代版本, 同样是单线程收集器,使用“标记-整理”算法
Parallel Old是Parallel Scavenge收集器的老年代版本, 使用多线程和“标记-整理”算法, 吞吐量优先, 主要与Parallel Scavenge配合在注重吞吐量及CPU资源敏感系统内使用
CMS是一种以获取最短回收停顿时间为目标的收集器(CMS又称多并发低暂停的收集器), 基于”标记-清除”算法实现, 整个GC过程分为以下4个步骤:
1. 初始标记(CMS initial mark)
2. 并发标记(CMS concurrent mark: GC Roots Tracing过程)
3. 重新标记(CMS remark)
4. 并发清除(CMS concurrent sweep: 已死对象将会就地释放, 注意:此处没有压缩)
CMS特点:
1. CMS默认启动的回收线程数=(CPU数目+3)/4,当CPU数>4时, GC线程一般占用不超过25%的CPU资源, 但是当CPU数<=4时, GC线程可能就会过多的占用用户CPU资源, 从而导致应用程序变慢, 总吞吐量降低.
2.无法处理浮动垃圾, 可能出现Promotion Failure、Concurrent Mode Failure而导致另一次Full GC的产生: 浮动垃圾是指在CMS并发清理阶段用户线程运行而产生的新垃圾. 由于在GC阶段用户线程还需运行, 因此还需要预留足够的内存空间给用户线程使用, 导致CMS不能像其他收集器那样等到老年代几乎填满了再进行收集. 因此CMS提供了-XX:CMSInitiatingOccupancyFraction参数来设置GC的触发百分比(以及-XX:+UseCMSInitiatingOccupancyOnly来启用该触发百分比), 当老年代的使用空间超过该比例后CMS就会被触发(JDK 1.6之后默认92%). 但当CMS运行期间预留的内存无法满足程序需要, 就会出现上述Promotion Failure等失败, 这时VM将启动后备预案: 临时启用Serial Old收集器来重新执行Full GC(CMS通常配合大内存使用, 一旦大内存转入串行的Serial GC, 那停顿的时间就是大家都不愿看到的了).
3.最后, 由于CMS采用”标记-清除”算法实现, 可能会产生大量内存碎片. 内存碎片过多可能会导致无法分配大对象而提前触发Full GC. 因此CMS提供了-XX:+UseCMSCompactAtFullCollection开关参数, 用于在Full GC后再执行一个碎片整理过程. 但内存整理是无法并发的, 内存碎片问题虽然没有了, 但停顿时间也因此变长了, 因此CMS还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction用于设置在执行N次不进行内存整理的Full GC后, 跟着来一次带整理的(默认为0: 每次进入Full GC时都进行碎片整理).
G1(Garbage-First)是一款面向服务端应用的收集器, 主要目标用于配备多颗CPU的服务器治理大内存.
- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS).
-XX:+UseG1GC启用G1收集器.
与其他基于分代的收集器不同, G1将整个Java堆划分为多个大小相等的独立区域(Region), 虽然还保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔离的了, 它们都是一部分Region(不需要连续)的集合.如:
每块区域既有可能属于O区、也有可能是Y区, 因此不需要一次就对整个老年代/新生代回收. 而是当线程并发寻找可回收的对象时, 有些区块包含可回收的对象要比其他区块多很多. 虽然在清理这些区块时G1仍然需要暂停应用线程, 但可以用相对较少的时间优先回收垃圾较多的Region. 这种方式保证了G1可以在有限的时间内获取尽可能高的收集效率.
G1的新生代收集跟ParNew类似: 存活的对象被转移到一个/多个Survivor Regions. 如果存活时间达到阀值, 这部分对象就会被提升到老年代.
其特定是:
一整块堆内存被分为多个Regions.存活对象被拷贝到新的Survivor区或老年代.年轻代内存由一组不连续的heap区组成, 这种方法使得可以动态调整各代区域尺寸.Young GC会有STW事件, 进行时所有应用程序线程都会被暂停.
多线程并发GC.
G1老年代GC特点如下:
并发标记阶段
1.在与应用程序并发执行的过程中会计算活跃度信息.
2.这些活跃度信息标识出那些regions最适合在STW期间回收(which regions will be best to reclaim during an evacuation pause).
3.不像CMS有清理阶段.
再次标记阶段
1.使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多.
2.空region直接被回收.
拷贝/清理阶段(Copying/Cleanup Phase)
1.年轻代与老年代同时回收.
2.老年代内存回收会基于他的活跃度信息.
JDK的常用命令:
jre中没有哦
1.jps
显示线程id和执行线程主类名
2.jps -l
显示线程id和执行线程的主类名的全路径
3.jps-m
显示线程id和执行线程的主类名及其参数
4.jstat -gc pid
查看垃圾回收状况
1 S0C: Survivor0(幸存区0)大小(KB) 2 S1C: Survivor1(幸存区1)1大小(KB) 3 S0U: Survivor0(幸存区0)已使用大小(KB) 4 S1U: Survivor1(幸存区1)已使用大小(KB) 5 EC : Eden(伊甸区)大小(KB) 6 EU : Eden(伊甸区)已使用大小(KB) 7 OC :老年代大小(KB) 8 OU : 老年代已使用大小(KB) 9 MC : 方法区大小(KB) 10 MU : 方法区使用大小(KB) 11 CCSC : 压缩类空间大小(KB) 12 CCSU : 压缩类空间使用大小(KB) 13 YGC:新生代GC个数 14 YGCT:新生代GC的耗时(秒) 15 FGC :Full GC次数 16 FGCT:Full GC耗时(秒) 17 GCT :GC总耗时(秒)
5.jvisualvm
JDK中最强大运行监视和故障处理工具,可以监控内存泄露、跟踪垃圾回收、执行时内存分析、CPU分析、线程分析
1.从github下载安装jvisualvm Visualgc插件 jvisualvm Visualgc:用来查看gc的插件
2.重启
3.通过配置启动