JVM 概述

261 阅读3分钟

1. JVM 架构

2. Class Loader

2.0 反射

Java 反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性与方法;对于任意一个对象,都能调用它的任意方法和属性;动态获取信息以及调用对象的方法的功能称为 Java 的反射机制。

2.1 类从编译到执行的过程

  1. 编译器将 xxx.java 编译成 xxx.class 字节码文件
  2. ClassLoader 将字节码文件转换成 JVM 中的 Class 对象
  3. JVM 利用 Class 对象实例化为 xxx 对象

2.2 ClassLoader 的种类

  1. BootStrapClassLoader:加载核心库 java.* (C++ 编写)
  2. ExtensionClassLoader:加载扩展库 javax.* (Java 编写)
  3. ApplicationClassLoader:加载程序所在目录 (Java 编写)

2.3 类的加载

2.3.1 类加载方式

  1. 隐式加载:new
  2. 显式加载:forName, loadClass
    • Class.forName 得到的 class 是已经初始化完成的
    • ClassLoader.loadClass 得到的 class 是还没有进行连接的

2.3.2 类装载过程

1. 加载
  • 通过 ClassLoader 加载 class 文件字节码,生成 Class 对象
2. 连接
  • 校验:检查加载的 class 的正确性和安全性
  • 准备:为类变量分配存储空间并设置变量初始值
  • 解析:JVM 将常量池内的符号引用转换为直接引用
3. 初始化
  • 执行类变量赋值和静态代码块

3. JVM 内存模型

3.1 按共享和独占分类

3.2 线程独占区域

3.2.1 程序计数器(PC 寄存器)(Program Counter Register)

  • 当前线程所执行的字节码行号指示器(逻辑)
  • 改变计数器的值来选取下一条需要执行的字节码指令
  • 和线程是一对一关系,即“线程私有”
  • 对 Java 方法计数,如果是 Native 方法则计数器值为 Undefined
  • 不会发生内存泄漏

3.2.2 Java 虚拟机栈(Stack)

栈帧

每有一个方法执行,就会产生一个新的栈帧

  • 局部变量表:执行过程中的所有变量
  • 操作数栈:入栈、出栈、复制、交换和产生消费变量
  • 动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属性方法的引用,持有这个引用是为了支持方法调用过程中的动态连接
  • 返回地址:方法执行过程中,有两种方式推出,1. 正常返回 2.异常退出

3.2.3 本地方法栈

3.3 共享区域

3.3.1 元空间 (MetaSpace) 与持久代 (PermGen) 的区别

  • 元空间使用本地内存,而持久代使用的是 JVM 的内存
元空间相对于持久代的优势
  • JDK7 和之前版本中,字符串常量池在持久代中,而持久代使用的是 JVM 内存,所以更容易发生性能问题内存溢出
  • 类和方法的信息大小难以确定,给永久代的大小指定带来困难
  • 永久代会给 GC 带来不必要的复杂性
  • 方便 HotSpot 与其他 JVM 如 Jroket 集成

3.3.2 堆 (Heap)

  • 对象实例的分配区域
  • GC 管理的主要区域

3.4 JVM 三大性能调优参数 -Xms -Xmx -Xss 的含义

  • -Xss:规定了每个线程虚拟栈的大小
  • -Xms:堆的初始值
  • -Xmx:堆的最大值

3.5 内存分配策略的区别

  • 静态存储:编译时确定每个数据目标在运行时的存储空间需求
  • 栈式存储:编译时数据区不确定,运行时模块入口前确定
  • 堆式存储:编译时和运行时都不确定,动态分配内存空间

3.6 栈和堆的区别

  • 管理方式:栈自动释放,堆需要 GC
  • 空间大小:栈比堆小
  • 碎片相关:栈产生的碎片比堆小
  • 分配方式:栈支持静态分配和动态分配,而堆只支持静态分配
  • 效率:栈的效率比堆高