一、JVM 的基本架构
JIT编译器 (Just-In-Time Compiler, 即时编译器):Client 或 Server
垃圾收集器:Serial、Parallel、CMS、G1
JVM Runtime:
32 位JVM 的最大内存约为2.5~3G .
64 位JVM 的指针压缩:-XX:+UseCompressedOops。64位寻址导致JVM内部Java对象表示(oops)的长度由32位变为64位,导致CPU高速缓存行中可用的oops变少,降低了CPU缓存的效率,使用压缩可以避免带来的损失。
二、JVM Runtime
命令行选项: -X 非标准项 , -XX 非稳定项
VM 生命周期: 启动、执行程序、检查和清理未处理异常、退出
VM 类加载:
字节码验证:
类数据共享:
解释器:
异常处理:
线程管理:
C++堆管理:
Java本地接口:
JVM致命错误管理:
三、JVM 垃圾回收
1、分代垃圾收集
弱分代假设:大多数分配对象的存活时间很短;存活时间久的对象很少引用存活时间短的对象。
新生代:一个Eden、两个Survivor。Minor GC 效率很。
老年代:Full GC 执行频率低,执行时间长。
永久代:存储元数据,例如类的数据结构、保留字符串等。
过早提升:Survivor中的存活对象溢出,多余的对象将被移到老年代,导致老年代中短期存活对象的增长,可能造成严重的性能问题。
提升失败:在Minor GC 中,如果老年代满了而无法容纳更多的对象,Minor GC 后将发生 Full GC。
2、垃圾收集器
Servial 收集器: 单线程。
新生代用复制算法,老年代使用标记-压缩算法。
Parallel 收集器: 吞吐量为先。
新生代用复制算法,老年代使用标记-压缩算法。
适用于批处理引擎、科学计算等。
CMS 收集器: 低延迟为先。
老年代使用标记-清理算法。
分为阶段:初始标记、并发标记、重新标记、并发预清除、并发清除
CMS 的缺点:
a、空间不连续,需要一个空闲列表记录;
b、需要更大的Heap处理浮动垃圾;
c、缺乏压缩,形成空间碎片化。
如果CMS 失败,将采用Stop-The-Worl 收集方法。
G1 收集器: CMS的替代者。G1中Heap不再划分成新生代、老年代,而是分为相同尺寸的区,优先收集垃圾最多的区。
3、应用程序对垃圾收集器的影响
内存分配:Eden满,发生Minor GC;老年代满,发生Full GC;内存分配速率越高、垃圾收集触发越频繁。
存活数据的多少:Heap 中存活对象越多,收集器需要做的工作越多。
老年代中的引用更新:若老年代中引用发生了更新,将创建一个Old-to-Young的引用,导致在预清除或重新标记阶段产生一个需要遍历的对象。一些不好的编程习惯会造成这个问题:对象池化(对象长期存活)、容器类的初始尺寸太小(内部数组需要长期调整尺寸)等。
四、JIT编译器
Client JIT: 启动快、编译快
Server JIT: 启动慢,但是启动后性能好,吞吐量高。
五、JVM自适应调优
Java 6 的自适应调优
32 位Linux 下 Client 模式:-client、SerialGC、-Xms(8M, 6M,1/64 RAM)、-Xmx(1/4 RAM)
64 位Linux 下 Client 模式:-server、ParallelGC、-Xms(1/64 RAM)、-Xmx(1/4 RAM - 1G)
32 位Linux 下 Server模式:-server、ParallelGC、-Xms(1/64 RAM)、-Xmx(1/4 RAM -1G)
64 位Linux 下 Server模式:-server、ParallelGC、-Xms(1/64 RAM)、-Xmx(1/4 RAM -1G)
Throughout 收集器自动调整新生代空间: -XX:+UseAdaptiveSizePolicy