记录JVM相关知识点
Javac的编译过程
词法分析
将关键字标识符转为符合Java语法的Token序列
语法分析
将Token序列整合为抽象语法树
语义分析
将语法树补充完善
生成字节码
根据语法树生成字节码
字节码文件
- 两种结构:无符号数和表
- 常量池,字段表,方法表,属性表等
HotSpot启动
- 可以通过gamma或者Java启动,两者实现相似
- Launcher启动过程
- main()->JavaMain()->InitializeJVM()->JNI_CreateJavaVM()
- LoadClass()和GetStaticMethodID()获取Java程序的启动类和启动方法
- jni_CallStatic VoidMethod()执行Java程序的main方法
- jni_DetachCurrentThread()断开与主线程的连接
- 全部非守护进程结束后调用jni_DestroyJavaVM()对JVM执行销毁
线程共享内存区
JVM根据受访问权限不同,分为线程共享和线程私有两大类。
线程共享内存区包括:
- 堆区
- JVM启动时创建,在实际内存中可以不连续
- 用于存储对象实例,垃圾回收重点区域。TaobaoVM创新之处在于GCIH,将生命周期比较长的实例放在Heap区以外
- 可以细分为新生代,老年代,新生代可以划分为Eden空间、From Survivor和To Survivor空间
- 方法区
- 存储每个Java类的结构信息,运行时常量池,字段和方法数据,构造函数和普通方法的字节码以及类,实例,接口初始化时需要用到的特殊方法等数据。
- 逻辑上独立,在物理上还是属于堆区
- 称为永久代,不会被频繁GC,可以显式指定是否需要回收方法区中的内容。GC的回收目标是常量池和类型卸载
- 运行时常量池
- 属于方法区的一部分
- 包含各种不同的常量
线程私有内存区
- PC寄存器
- 不是物理寄存器,可以理解为PC计数器
- 线程私有,与线程生命周期一致
- 存储正在执行的字节码地址,如果是native方法,PC寄存器为空
- 没有明确规定需要跑出OutofMemoryError异常的运行时内存区
- Java栈
- 存储栈帧,栈帧存储的是局部变量表,操作数栈以及方法出口等信息
- 栈可以被实现成固定大小或者可动态扩展的内存大小
- 本地方法栈
- 用于支持native方法
性能监控区
JVM提供是一块内存共享区,专供外部程序访问这块区域中的Perf Data
自动内存管理
- 内存分配原理
- JVM三种引用类型:类类型(class type),数组类型(array type),接口类型(interface type)。
- new 一个对象的过程:判断new的参数能否在常量池中找到换一个符号引用,检查与这个符号引用相对应的类是否已经成功经历过加载,解析和初始化的步骤;完成装载之后,就可以确定需要分配的内存大小;jvm对其进行内存分配,以存储所生成的对象实例。
- 为新对象分配内存方法:指针碰撞或者空闲列表
- 直接从堆区划分内存是非线程安全的
- 如果一个类在内存装载之前已经成功完成类装载步骤,JVM就会优先选择在TLAB(Thread Local Allocation,本地分配缓存区)中为对象实例分配内存空间,TLAB是Java堆区中的一块线程私有区域,它包含在Eden区间中。这种称为快速分配策略。
- -XX:UseTLAB设置是否开启TLAB空间,很小,占据Eden区间的1%,可以通过设置参数扩容
- 如果TLAB分配失败,则使用加锁在Eden空间中分配。Eden空间不足,执行MinorGC
- 内存分配完成之后对空间进行零值初始化。之后再执行初始化对象头和实例数据
- 最后将引用内存入栈
- 逃逸分析和栈上分配
- 逃逸分析:JVM在执行性能优化之前的一种分析技术,可以分析对象的作用域。
- 栈上分配:如果一个实例作用域在一个方法类,可以直接把对象压栈,随着方法结束自动销毁栈帧。
- 对象内存布局和OOP-Klass模型
- 对象实例化包括初始化对象头和实例数据
- 对象头包括阿Mark word和元数据指针等数据。
- Mark Word用于存储对象运行时信息,包括HashCode,GC分代年龄,锁状态信息,线程持有锁,偏向线程ID,偏向时间戳
- 元数据指向方法区中目标类的类型信息。
- 实例数据,存储定义在当前对象中各种类型的字段信息,包括父类的字段信息。
- OOP-Klass:表示Java类在虚拟机中的对等实体。(这部分可以看书)
- GC
- 负责内存的动态分配和垃圾回收
- 垃圾碎片通过压缩算法来消除
- 垃圾标记:引用计数和根搜寻法
- 引用计数的问题:某些无用对象之间相互引用,造成无法回收。
- 只有被根对象集合中的对象直接或者间接引用才算是存活对象。包含五类对象:Java栈中引用对象,本地方法栈中的对象引用,静态常量池中的对象引用,类静态属性的对象引用,与一个类对应的唯一数据类型的Class对象。
- 垃圾收集算法
- 标记-清除
- 效率低,容易造成内存碎片
- 复制算法
- Minor GC表示年轻代GC
- 将To Survivor区域作为一个临时的空间交换角色
- 将一次Minor GC后Eden和From Survivor区剩下的对象复制到To Survivor区域,然后将From Survivor区和To Survival区互换。
- 例外情况:如果From Survivor中的实例达到了指定分代年龄或者To Survivor中内存不够,则直接将实例复制如老年区。
- 标记-压缩
- 将标记的对象实例移动到一个规整切连续的区域,然后再实行Full GC,利用指针碰撞技术实现。
- 标记-清除
- 垃圾收集器
- 分为串行回收还是并行回收,是并发还是”Stop-the-world”机制
- Serial/Serial Old
- 复制算法、串行回收、Stop-the-world机制
- Serial Old用于老年代,采用的是标记-压缩算法、串行回收、Stop-the-world机制
- ParNew收集器
- Serial的多线程版本,单CPU下不一定会比Serial更好
- Parallel收集器
- Parallel在新生代中同样采用了标记-复制算法,并行回收,stop-the-world
- 与ParNew不同的是可以控制吞吐量大小,可以通过参数设置GC的执行频率。
- Parallel Old采用的是标记-压缩算法
- CMS收集器
- 采用标记-清除算法,采用空闲列表分配内存。可以通过参数来调整压缩。
- 初始标记阶段:Stop-the-World标记不可达对象
- 并发标记阶段:将不可达对象标记为垃圾对象,可能由于并发改变了对象的引用关系。
- 再次标记阶段:Stop-the-World,再次短暂暂停。标记出改变引用关系的部分。
- 并发清除阶段: 清除垃圾对象
- Full GC在其它收集器中回收所有的堆空间,而CMS的收集器可以指定当老年代达到一定比例之后再进行内存回收
- 区域化分代式:G1收集器
- 将Java堆区划分为2048个大小相同的独立Region块,每个Region块之间不连续,大小在1M和32M之间。优先回收占用内存块大的Region
- 6个阶段:初始标记,根区域扫描,并发标记,再次标记,清除,拷贝
- GCHisto
- 可以使用这个程序进行日志分析
如何定位堆溢出的代码段
- 通过设置vm argument转储堆内存快照
- -Xx:+HeapDumpOnOutOfMemoryError
- 需要使用MemoryAnalyzer 来分析
JVM监控工具
jdk的bin里面有JConsole
类的加载机制
- 类加载器
- 主要任务是根据一个类的全限定名来读取此类的二进制字节流到JVM内部,然后转换为一个与目标类对应的java.lang.Class对象实例。
- 分为两类:引导类加载器,自定义类加载器
- Bootstrap ClassLoard:C++编写,嵌在JVM内部,主要负责加载JAVA_HOME/lib目录中的所有类型,或者由-Xbootclasspath指定路径中的所有类型。
- ExtClassLoader:派生与ClassLoader,Java语言编写,负责加载JAVA_HOME/lib/ext中的所有类型。
- AppClassLoader:负责加载ClassPath中的所有类型。
- ClassLoader
- 可以自定义ClassLoader
- 双亲委派模型
- 除了启动类加载器之外,每个类加载器都有一个超类加载器。
- 类加载器接到一个类加载的任务之后,并不会立即展开加载,而是将加载任务委派给它的超类加载器去执行,每一层都采用相同的方式,直到委派给最顶层的启动加载类为止。
- Tomcat不是使用的双亲委派加载,而是优先自己加载。
- 类加载过程
- 加载
- 连接
- 验证:格式验证,语义分析,操作验证等。
- 准备:为类中所有静态变量分配内存空间,并为其设置初始值
- 解析:将常量池中的所有符号引用转变为直接引用。JVM会在这个阶段执行static代码块中定义的所有操作。
- 初始化
- 六种时机,进行类的初始化。
- 字节码文件加密与解密
Java执行引擎
- Java栈帧
- 存储局部变量表,操作数栈,动态链接和方法返回值
- 局部变量表
- 存储方法参数,对象引用,以及returnAddress类型
- 所需容量在编译期就能完全确定
- 方法外的变量存在对象实例空间中
- 操作数栈
- 通过入栈出栈来完成操作
- 动态链接
- 常量池表,描述一个方法调用了另外的方法时,就是通过常量池中指向方法的符号引用来表示的。动态链接将这些符号引用转换为直接引用。
- 方法返回值
- 包括正常返回和异常返回
Java
RunTime类为JVM创建,可以获取JVM的相关信息,并能执行本机可执行程序。
Maven
maven相关知识点
POM配置信息
- project dependencies
- plugins
- goals
- build profiles
- project version
- developers
- mailing list
构建周期
阶段 | 处理 | 描述 |
---|---|---|
prepare-resources | 资源拷贝 | 本阶段可以自定义需要拷贝的资源 |
compile | 编译 | 本阶段完成源代码编译 |
package | 打包 | 本阶段根据 pom.xml 中描述的打包配置创建 JAR / WAR 包 |
install | 安装 | 本阶段在本地 / 远程仓库中安装工程包 |
当需要在某个特定阶段之前或之后执行目标时,可以使用 pre 和 post 来定义这个目标。
Maven 有以下三个标准的生命周期:
- clean
- default(or build)
- site
clean
- pre-clean
- clean
- post-clean
build
生命周期阶段 | 描述 |
---|---|
validate | 检查工程配置是否正确,完成构建过程的所有必要信息是否能够获取到。 |
initialize | 初始化构建状态,例如设置属性。 |
generate-sources | 生成编译阶段需要包含的任何源码文件。 |
process-sources | 处理源代码,例如,过滤任何值(filter any value)。 |
generate-resources | 生成工程包中需要包含的资源文件。 |
process-resources | 拷贝和处理资源文件到目的目录中,为打包阶段做准备。 |
compile | 编译工程源码。 |
process-classes | 处理编译生成的文件,例如 Java Class 字节码的加强和优化。 |
generate-test-sources | 生成编译阶段需要包含的任何测试源代码。 |
process-test-sources | 处理测试源代码,例如,过滤任何值(filter any values)。 |
test-compile | 编译测试源代码到测试目的目录。 |
process-test-classes | 处理测试代码文件编译后生成的文件。 |
test | 使用适当的单元测试框架(例如JUnit)运行测试。 |
prepare-package | 在真正打包之前,为准备打包执行任何必要的操作。 |
package | 获取编译后的代码,并按照可发布的格式进行打包,例如 JAR、WAR 或者 EAR 文件。 |
pre-integration-test | 在集成测试执行之前,执行所需的操作。例如,设置所需的环境变量。 |
integration-test | 处理和部署必须的工程包到集成测试能够运行的环境中。 |
post-integration-test | 在集成测试被执行后执行必要的操作。例如,清理环境。 |
verify | 运行检查操作来验证工程包是有效的,并满足质量要求。 |
install | 安装工程包到本地仓库中,该仓库可以作为本地其他工程的依赖。 |
deploy | 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程。 |
Site 生命周期
Maven Site 插件一般用来创建新的报告文档、部署站点等。
阶段:
- pre-site
- site
- post-site
- site-deploy
构建配置文件
构建配置文件是一组配置的集合,用来设置或者覆盖 Maven 构建的默认配置。使用构建配置文件,可以为不同的环境定制构建过程,例如 Producation 和 Development 环境。
profile类型
Profile 主要有三种类型。
类型 | 在哪里定义 |
---|---|
Per Project | 定义在工程 POM 文件 pom.xml 中 |
Per User | 定义在 Maven 设置 xml 文件中 (%USER_HOME%/.m2/settings.xml) |
Global | 定义在 Maven 全局配置 xml 文件中 (%M2_HOME%/conf/settings.xml) |
maven插件
Maven 实际上是一个依赖插件执行的框架,每个任务实际上是由插件完成。Maven 插件通常被用来:
- 创建 jar 文件
- 创建 war 文件
- 编译代码文件
- 代码单元测试
- 创建工程文档
- 创建工程报告
类型 | 描述 |
---|---|
Build plugins | 在构建时执行,并在 pom.xml 的 元素中配置。 |
Reporting plugins | 在网站生成过程中执行,并在 pom.xml 的 元素中配置。 |
插件 | 描述 |
---|---|
clean | 构建之后清理目标文件。删除目标目录。 |
compiler | 编译 Java 源文件。 |
surefile | 运行 JUnit 单元测试。创建测试报告。 |
jar | 从当前工程中构建 JAR 文件。 |
war | 从当前工程中构建 WAR 文件。 |
javadoc | 为工程生成 Javadoc。 |
antrun | 从构建过程的任意一个阶段中运行一个 ant 任务的集合。 |