JVM

​ 记录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

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 安装 本阶段在本地 / 远程仓库中安装工程包

当需要在某个特定阶段之前或之后执行目标时,可以使用 prepost 来定义这个目标

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 任务的集合。
-------------本文结束 感谢您的阅读-------------
作者GonewithGt
有问题请 留言 或者私信我的 微博
满分是10分的话,这篇文章你给几分