一、jvm组成部分
名称 | 内容 | 备注 |
堆 | 数组,对象、分为新生代、存活区、老年代 | 新生代:存活区 8:2 新生代:老年代 1:2
|
栈 | 局部变量表、对象的引用、操作数栈、方法出口 | |
程序计数器 | 行号指示器、通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成,也就是记录当前线程的执行点,调度线程执行 | |
方法区 | 类的编译信息,静态变量,常量 | |
直接内存 | nio操作 在jdk1.4中新加入了NIO类,他可以调用native函数库直接分配堆外内存,然后通过java堆中的DirectByteBuffer 对象来指向这块内存,进行内存分配等工作。 | |
二、参数配置
名称 | 参数 | 参数说明 |
堆 | -Xms:堆最小值(新生代,老年代之后) -Xmx:堆最大值(新生代,老年代之后) -Xmn指定eden区的大小 -XX:SruvirorRation调整幸存区的大小 -XX:PretenureSizeThreshold设置进入老年代的阈值 | 例:-Xms1024 -Xmx1024 通常将-Xms与-Xmx设置为一样大小来减小gc的次数,堆内存不足时抛出OutOfMemoryError异常 |
栈 | -Xss | 例:-Xss128k 单线程下无论栈帧太大还是栈容量太小,及应用深度超过虚拟机允许深度都会抛出StackOverflowError每个方法压入栈的帧大小是不一致的。多线程下当每个线程分配栈帧太大内存不够扩展时抛出OutOfMemoryError异常线程栈帧越大,可创建的线程越少 |
方法区 | -XX:PermSize方法区内存最小值 -XX:MaxPermSize方法区内存最大值 | 例: -XX:PermSize=20M -XX:MaxPermSize=20M
各个线程共享的内存区域,主要用来存储类的元数据、常量、静态变量、即时编译后的代码 异常类型 OutOfMemoryError 原因:常量过多,或代理反射等使用频繁 |
直接内存 | -XX:MaxDirectMemorySize | 例: -XX:MaxDirectMemorySize=10M 不足时抛出OutOfMemory异常 |
辅助参数 | -XX:+HeapDumpOnOutOfMemoryError 打印堆内存异常时快照信息 -XX:+HeapDumpPath快照输出路径 - | |
三、垃圾回收算法
名称 | 优缺点 | 备注 |
标记--清除算法(Mark-Sweep) | 算法执行分为两个阶段标记与清除,所有的回收算法基本都是基于标记回收算法深度优化 缺点:效率问题,内存空间碎片(不连续的空间) | |
复制回收算法(包括分代算法) | 比较标记清除算法,避免回收造成的碎片化问题 缺点:以局部的内存空间牺牲为代价,不过空间的浪费比较小,默认8:1比例1是浪费的,复制也有一定的效率与空间成本 | |
标记整理算法(Mark-Compact) | 避免了空间的浪费,与内存碎片问题 缺点:整理时复制有效率成本 |
四、垃圾收集器
名称 | 用法 | 备注 |
Serial(串行GC) | -XX:+UseSerialGC | 串行收集器采用单线程stop-the-world的方式进行收集。当内存不足时,串行GC设置停顿标识,待所有线程都进入安全点(Safepoint)时,应用线程暂停,串行GC开始工作,采用单线程方式回收空间并整理内存。单线程也意味着复杂度更低、占用内存更少,但同时也意味着不能有效利用多核优势。事实上,串行收集器特别适合堆内存不高、单核甚至双核CPU的场合。 |
Serial Old收集器(串行GC) | -XX:+UseSerialGC | |
ParNew(并行GC) | -XX:+UseParNewGC | 新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下悠着比Serial更好的表现 |
Parallel Scavenge(并行回收GC) | -XX:UserParallelGC -XX:parallelGcThreads=4指定线程数 | 并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般为99%,吞吐量=用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互响应要求不高的厂家,是server级别默认采用的GC方式 |
Parallel Old | -XX:+UseParallelOldGC | Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量有限 |
CMS(并发GC) | -XX:+UseConcMarkSweepGC | 年轻代ParNew与并行收集器类似,而老年代CMS每个收集周期都要经历:初始标记、并发标记、重新标记、并发清除。其中,初始标记以STW的方式标记所有的根对象;并发标记则同应用线程一起并行,标记出根对象的可达路径;在进行垃圾回收前,CMS再以一个STW进行重新标记,标记那些由mutator线程(指引起数据变化的线程,即应用线程)修改而可能错过的可达对象;最后得到的不可达对象将在并发清除阶段进行回收。值得注意的是,初始标记和重新标记都已优化为多线程执行。CMS非常适合堆内存大、CPU核数多的服务器端应用,也是G1出现之前大型应用的首选收集器。 但是CMS并不完美,它有以下缺点: 由于并发进行,CMS在收集与应用线程会同时会增加对堆内存的占用,也就是说,CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大停顿时间; |
G1 | -XX:+UseG1GC
|
五、常用组合
Serial/Serial Old | 年轻代Serial收集器采用单个GC线程实现"复制"算法(包括扫描、复制) 年老代Serial Old收集器采用单个GC线程实现"标记-整理"算法 Serial与Serial Old都会暂停所有用户线程(即STW) 适用场合: CPU核数<2,物理内存<2G的机器(简单来讲,单CPU,新生代空间较小且对STW时间要求不高的情况下使用) -XX:UseSerialGC:强制使用该GC组合 -XX:PrintGCApplicationStoppedTime:查看STW时间 |
ParNew/Serial Old | 与上边相比,只是比年轻代多了多线程垃圾回收而已 |
ParNew/CMS | 当下比较高效的组合 |
Parallel Scavenge/Parallel Old | 自动管理的组合 年轻代Parallel Scavenge收集器采用多个GC线程实现"复制"算法(包括扫描、复制)年老代Parallel Old收集器采用多个GC线程实现"标记-整理"算ParallelScavenge与Parallel Old都会暂停所有用户线程(即STW) 适用于 很多的CPU计算任务而用户交互任务较少的情况不想自己去过多的关注GC参数,想让虚拟机自己进行调优工作 |
G1 | 最先进的收集器,但是需要JDK1.7update14以上 |
六、调优方法
1.预留新生代空间
由于fullGC的成本远比minorGC的成本打,所以给应用分配一个合理的新生代空间,尽量将对象分配到新生代减少fullGC的频率
2.大对象进入老年代
将大对象直接分配到老年代,保持新生代对象的结构完整性,以提高GC效率,以通过-XX:PretenureSizeThreshold设置进入老年代的阀值
3.稳定与震荡的堆大小
稳定的对大小是对垃圾回收有利的,方法将-Xms和-Xmx的大小一致
4.吞吐量优先
尽可能减少系统执行垃圾回收的总时间,故采用并行垃圾回收器XX:+UseParallelGC或使用-XX:+UseParallelOldGC
5.减低停顿
使用CMS回收器,同时减少fullGC的次数
七、调优工具
jps | 虚拟机进程状况工具 |
jstat | 虚拟机统计信息监视工具 |
jmap | java内存印象工具 |
jhat | 虚拟机堆转储快照分析工具 |
jstack | java线程堆栈跟踪工具 |
jinfo | java配置信息工具 |
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,CG耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3S,或者平方GC,则必须优化。如果满足下面的指标,则一般不需要进行GC优化:
(1)Minor GC执行时间不到50ms;
(2)MinorGC执行不频繁,约10秒一次;
(3)Full GC执行时间不到1s
(4)FullGC执行平路不算频繁,不低于10分钟1次