JVM调优基础
一、JIT
java同时具有解释型语言的跨平台性,又有编译型语言的本地性能
两次编译:第一次被编译成java字节码,JVM可以直接解释执行Java字节码,也可以用JIT先编译成本地二进制汇编语言,再执行
-client:运行开始时就编译
-server:运行后编译,可以更好地进行优化
-XX:TieredCompilation 分层编译,先client-再server java8默认开启 java8之前分层编译有BUG
在64位JVM使用-client,JVM仍会使用-d64
而在32位JVM使用-d64, 会报错
代码缓存:JIT编译后的二进制代码的缓存,当缓存满时,新的代码只能解释执行
满时警告:CadeCache is full
调整大小:-XX:ReservedCodeCacheSize=N java7默认96M
其他:编译阈值等用默认
当方法或循环达到编译阈值时,就会进入编译队列,异步进行代码编译,此时代码仍可解释执行;当下次调用方法或者循环进入下一轮时,会调用编译后的代码。
内联:热点的getter, setter在编译时会被直接改成p.name对对象变量的引用
默认开启
逃逸优化:对于不存在线程共享的同步代码,会去掉synchronized
逆优化:丢弃编译后的二进制代码
先前优化的代码失效时:对象已经改变
分层编译:client——server切换
僵尸代码:长时间没有使用的代码会被丢弃
二、GC
GC调优主要是减少GC的stop-the-world时间
一、内存划分
young=eden+survivor
1.Eden满后执行Minor GC, 存活的对象被堆积在同一Survivor
2.当一个Survivor满后,其中存活的对象全部进入另一个Survivor,同时对象年龄+1,
此过程中,年龄达到MaxTenuringThreshold (最大/占据/门槛)的对象进入Old
-->必有一个Survivor是空的
一些较大的对象,需要直接分配一块较大的连续内存空间,则直接进入到老年代
old
Full GC - Major GC 时间是Minor GC 10倍以上
老年代对新生代对象的引用:
老年代中存在一个cardtable,所有老年代对象指向新生代对象的引用都会被记录在这个表中。
当新生代执行GC的时候,只需要查询card table来决定是否可以被回收,而不用查询整个old
permanent - 持久代又称方法区
保存常量、字符串常量、class对象
class对象被回收的条件十分严苛:
1 所有实例被回收
2 加载该类的ClassLoader被回收
3 Class对象无法通过任何途径访问(包括反射)
java8中将永久代变为了元空间:
元空间只保存对JIT和JVM运行有关的数据,Class,反射对象等被转移到普通堆空间了
元空间可以使用的内存是无限的,不需要参数调整
二、GC算法
1、根搜索算法 - 从root引用不到的对象,为不可达对象,将被GC
可以作为Root的对象有:
虚拟机栈中引用的对象-本地变量表
本地方法栈中引用的对象-Native方法
方法区静态属性引用的对象
方法区常量引用的对象
2、标记-清除算法 - 先扫描所有对象,标记所有存活,再清除所有未被标记的对象
在存活对象很多时适合,因为不用移动存活对象
不会移动对象,高效但会导致内存碎片
3、复制算法 - 将内存分为两个区,每次只使用一个区
在存活对象很少时适合,因为要移动所有存活对象
50%的内存浪费
4、标记-整理算法 - 比2多了一步整理
Major GC一般使用标记-整理算法
Minor GC一般使用复制算法
三、回收器 - 每个回收器都存在stop-the-world问题
1、Serial (-XX:+UseSerialGC) 用于年轻代
使用:复制算法
2、SerialOld (-XX:+UseSerialGC) 用于老年代
使用:标记-整理算法
*3、ParNew (-XX:+UseParNewGC) 用于年轻代
使用:复制算法
Serial的多线程版本
*4、ParallelOld(-XX:+UseParallelOldGC) 用于老年代
使用:标记-整理算法
Serial的多线程版本
吞吐量优先
5、ParrallelScavenge (-XX:+UseParallelGC) 用于年轻代
使用:复制算法
可控吞吐量(吞吐量=用户代码执行时间/用户代码执行时间+GC时间
*6、CMS (-XX:+UseConcMarkSweepGC) 用于老年代
*使用:标记-清理算法
初始标记(STW) - 并发标记 - 并发预清理 - 重新标记(STW) - 并发清理 - 并发重置
初始标记:STW-找到和根对象直接关联的对象
并发标记:找到和初始标记对象关联的对象
并发预处理:找到在并发标记阶段晋升的对象,减少下一阶段重新标记的工作量
重新标记:STW-重新从根对象开始查找并标记并发阶段遗漏的对象
并发清理:--
并发重置:重置CMS状态
缺点:
内存碎片
GC线程和应用线程并发执行,消耗更多CPU资源,吞吐量低(存在线程切换)
内存浪费-默认68%就出发GC XX:CMSinitiatingOccupandcyFraction=n
如果CMS后台线程无法获得完成GC的CPU资源,或者内存碎片严重无法找到连续空间分配内存,
CMS会蜕化成Serial收集器,进行一次Serial收集。之后又变回CMS收集
7、G1收集器 - 用于老年代和年轻代,是商用收费的收集器
适合处理超大堆
将堆划分为若干个区域Region
老年代也被划分为多个Region,采用复制算法
参数调整
1.堆大小
-Xms4096m
-Xmx4096m
2.代空间
-XX:NewRatio=N youngSize = heapSize / 1+N
-XX:NewSize=1024m -XX:MaxNewSize=1024m 显示指定young代大小
永久代
-XX:PermSize=N -XX:MaxPermSize=N
元空间
不需要调整
3.收集器
吞吐量优先:
-XX:+UseParallelGC
-XX:+UseParallelOldGC
响应优先:
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
参数:
自动调整:
JVM运行过程中,会根据以往的性能历史,自动调整堆、代已经Survivor大小,
默认开启,可以用-XX:-UseAdaptiveSizePolicy关闭
如果将-Xms -Xmx设置成一样的值,-XX:NewSize -XX:MaxNewSize设置成一样的值,那么自动调节功能将被关闭
-XX:ParallelGCThreads 会根据CPU核心数自动调整
日志:
-XX:+PrintGCDetails 打印详细GC日志
-XX:+PrintGCTimeStamps 打印GC日志生成的时间
-Xloggc:filename 限定GC日志路径,默认是标准输出(命令行)