
Class File
语法分析 -> 语法分析器 -> 抽象语法树 -> 语义分析器 -> 注解 -> 字节码生成器
javac HelloWork.class -> class文件
javap -v -c -p HelloWork.class -> 反编译生成 HelloWork.txt
类加载机制
- 装载 Loading
- Bootstrap ClassLoader:加载jdk包 jre/lib/rt.jar 所有的class
- Extension ClassLoader:加载扩展包 jre/lib/*.jar
- App ClassLoader:加载classpath中指定的jar
- Custom ClassLoader:加载java.lang.ClassLoader的子类自定义的class
- 链接 Linking
- (检验)保证类加载的正确性:格式校验,语义校验,字节码校验,符号引用校验
- (准备)类的静态变量分配内存,并初始化默认值
- (解析)类的符号引用转化为直接引用(逻辑地址转为物理地址)
- 初始化 Initialization
- 对类的静态变量,静态代码块初始化默认值
- 使用 Using
- 卸载 Unloading
双亲委派机制
自底向上,从custom到bootStrap逐层检查,只要某个ClassLoader已加载,就认为加载此类,保证只加载一次。自顶向下,也就是由上层逐层尝试加载此类。
- 为什么要设计双亲委派机制
- 沙箱安全机制
- 避免重复加载
- 如何破坏双亲委派机制
- tomcat 自定义类加载器
- SPI机制
- OSGI
JVM 架构

运行时数据区
- Method Area 方法区 (非堆)
- 存储类信息,常量,静态变量,编译后的代码等元数据,类的定义
- String 常量存在哪里?
- JDK1.7存方法区
- JDK1.8存堆中
- Heap(堆)
- 虚拟机启动时创建,被所有线程共享
- java对象实例化及数组都在堆上分配
- Java Virtual Machine Stacks(Java 虚拟机栈)
- 线程执行的区域,保存着一个线程中方法的调用状态。
- 线程私有的,独立的,随着线程的创建而创建
- 每一个被线程执行的方法,栈中称为栈帧,也就是每个方法对应一个栈帧
- 调用一个方法,就会向栈中压入一个栈帧,一个方法调用完成,就会把栈帧从栈中弹出
- Native Method Stacks(本地方法栈)
- java调用的Native方法
- The PC Register(程序计数器)
- 线程执行java方法,计数器记录正在执行的虚拟机字节码指令地址,如果时Native方法,计数器为空。

JVM内存模型
- 线程私有:虚拟机栈,本地方法栈,程序计数器
- 线程共享:方法区(非堆)和堆
- 新对象申请内存空间
- Eden是否有足够空间?
- (MinorGC)-> JVM回收不活跃对象
- Eden是否有足够空间?
- Survivor区是否有空间?
- Eden区活跃对象复制到Survivor区
- Old区是否有足够空间?
- 将Survivor区活跃对象复制到Old区
- (FullGC)-> JVM回收不活跃对象
- OutofMemeoryError
- Eden是否有足够空间?

垃圾收集
- 确认垃圾对象
- 引用计数法:对象没有任何指针对其引用,它就是垃圾。(存在互相引用场景)
- 可达性分析法:通过GC Root对象,往下寻找,看某个对象是否可达。
- 垃圾收集算法
- 标记清除:先标记需要回收的对象,直接清除掉,释放空间(Old)
- 缺点:效率不高,存在内存碎片
- 标记整理:先标记,再将标记的移动到相邻的空间中(Old)
- 优点:内存空间连续,缺点的,耗费两个区域空间(S0,S1)
- 标记复制:先标记,再复制。(young)
- 缺点:大量复制操作,效率降低,空间利用率降低
- 标记清除:先标记需要回收的对象,直接清除掉,释放空间(Old)
- 垃圾收集器
- Serial:串行收集器
- Parallel:并行收集器(关注于吞吐量的收集器)(类似Serial的多线程模式)
- CMS:并发收集器,低停顿用于Old
- 初始标记:GC Root直接关联
- 并发标记:GC Root追踪关联对象
- 重新标记:并发标记变动内容(STW)
- 并发清除:清除不可达对象回收空间
- G1:也是并发收集器,更关注停顿时间(和可配置的停顿时间)
- 初始标记:GC Roots标记对象(停止用户线程可自定义停顿时间)
- 并发标记:GC Root追踪关联对象
- 最终标记:修正标记变动内容(STW)
- 筛选回收:Region回收价值成本排序,根据期望停顿时间制定回收策略
- ZGC:jdk11,10ms停顿时间要求,支持TB级别,64位操作系统使用
JVM 参数
官方文档:docs.oracle.com/javase/8/do…
- Boolean
- -XX:+UseConcMarkSweepGC
- -XX:+UseG1GC
- 非Boolean
- -XX:MaxGCPauseMillis=500
- -Xms1000M 等价于 -XX:InitialHeapSize=1000M
- -Xmx1000M 等价于 -XX:MaxHeapSize=1000M
- -Xss100 等价于 -XX:ThreadStackSize=100
- 查看参数
- 启动时添加 +PrintFlagsFinal
- 通过jinfo查看
- 参数建议优化
- ‐XX:MaxTenuringThreshold:自适应GC大小,默认值15,CMS为6
- ‐XX:PretenureSizeThreshold:超过多大的对象直接在老年代分配
- ‐XX:+/‐ UseAdaptiveSizePolicy:是否启动自适应生成大小调整(默认启用)
- ‐XX:SurvivorRatio:S区比例默认为8
- ‐XX:ConcGCThreads:并发GC的线程数(默认cpu数量)
- ‐Xsssize:线程堆栈大小
- ‐Xms和‐Xmx :初始堆和最大堆。两者值一样,防止动态扩容
- ‐XX:ReservedCodeCacheSize:JIT编译空间缓存大小
- G1调优建议
- 不要手动设置新生代和老年代大小,只要设置整体的堆大小
- 不断调优停顿时间为目标
- 使用 -XX:ConcGCThreads=n 来增加标记线程的数量
- 适当增加堆内存大小
- 每一次 FULL GC 都需要关注原因
JVM 命令
官方文档:docs.oracle.com/javase/8/do…
- jps
- jinfo
- jinfo -flag name PID
- jinfo -glags PID
- jstat
- jstat -class PID 1000 10 查看类加载信息
- jstat -gc PID 1000 10 查看GC信息
- jstack
- jmap
- jmap -heap PID
- jmap -dump:format=xx,file=heap.hprof PID
分析工具
- JDK
- jconsole
- jvisualvm
- JVM (分析下dump文件)
- Arthas:下载jar包,直接运行使用
- MAT:工具下载
- HeapHero:heaphero.io
- Perfma:console.perfma.com
- GC
- gcviewer:下载jar包
- gceasy:gceasy.io
- gcplot:it.gcplot.com