Java虚拟机知识框架图


内容概述:

  1. JVM运行时内存结构
  2. 垃圾收集器和内存分配
  3. Class类文件结构
  4. JVM类加载机制
  5. 字节码执行引擎
  6. 编译期优化和运行期优化
  7. 高效并发

JVM知识框架:

JVM知识框架


1. JVM运行时内存结构

1.1 线程私有:
  • 程序计数器:没有任何OutOfMemoryError
  • 虚拟机栈:-Xss, 如果栈请求超过虚拟机允许的深度抛出StackOverFlowError, 如果虚拟机栈可以动态扩展,但是无法申请到足够的内存时会抛出OutOfMemoryError.
  • 本地方法栈:HotSpot虚拟机把本地方法栈和虚拟机栈合并
    1.2 线程共享:
  • 堆:对象实例和数组在这里分配,ThreadLocalAllocationBuffer. 但是堆上分配(Stack Allocation)对象并不是那么绝对了,例如利用逃逸分析技术,如果判定对象不会逃逸出方法之外,那就让这个对象直接在虚拟机栈上分配,从而可以减少垃圾收集系统的压力.
  • 方法区(包含运行时常量池ConstantPool):存储类信息,常量,静态变量,JIT编译后的代码等
    1.3 直接内存(不属于JVM运行时数据区)

2. 垃圾收集器和内存分配

2.1 判断对象是否已死?: what?
  • 引用计数
  • 可达性分析
    GC Roots:
  • 虚拟机栈栈帧中引用的对象
  • 方法区中类static属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中Native方法引用的对象

    2.2 何时进行回收: when?
    2.2.1 回收堆区: 对象实例和数组
    生存还是死亡?
    2.2.2 回收方法区: 废弃常量和无用的类
    判定无用的类:
  • 该类中的所有实例都已经被回收
  • 加载该类的ClassLoader已经被回收
  • 该类的java.lang.Class对象没有在任何地方被引用(反射)
    在大量使用反射, 动态代理,CGLib等byteCode框架,动态生成JSP以及频繁自定义ClassLoader的场景都需要虚拟机卸载类,回收方法区

    2.3 垃圾收集算法: how?
    分代收集理论: 根据对象存活的周期不同,分为新生代和老年代
    收集算法:
  • 标记-清除算法[Mark-Sweep]: 最原始的想法,效率和碎片问题
  • 复制算法[Copy]: 提升效率,没有碎片,但是浪费空间,存活率较高的时候要复制对象很多,效率降低
  • 标记-整理算法[Mark-Compact]:老年代适合这种方法

    2.4 垃圾收集器
    新生代收集器[Minor GC]:
  • serial : 单线程,stop the world
  • ParNew : 多线程,
  • Paralle Scavenge : 吞吐量优先
    老年代收集器[Full GC]:
  • Serial Old
  • Parallel Old
  • CMS[Concurrent Mark Sweep] : 初始标记(stop),并发标记,重新标记(stop),并发清除
    不分代收集全: G1[Garbage First] : 整体看属于标记-清除,局部看属于复制算法

    2.5 内存分配策略
  • 对象优先在Eden区分配
  • 大对象直接进入老年代
  • 长期存活对象进入老年代
  • 空间分配担保

3. Class类文件结构


4.JVM类加载机制

JVM把.class文件加载到内存,并对数据进行校验,准备,解析和初始化,最终形成可以被JVM直接使用的Java类型.

1
加载--->连接(验证,准备,解析)--->初始化--->使用--->卸载

类加载的时机, 有且只有5种情况必须立即进行”初始化”:

  • 遇到new, getstatic, putstatic, invokestatic这4条指令。常见的场景是new创建实例,读或者写一个类的非final字段,调static方法.
  • 反射
  • 初始化子类时必须先初始化其父类
  • 虚拟机启动时, 用户要指定一个执行的主类.
  • java.lang.invoke.MethodHandle
1. 加载(loading)
  • 通过类的全限定名来读取二进制字节流
  • 将字节流代表的静态结构转换为方法区的数据结构
  • 在内存中生成java.lang.Class对象,对HotSpot而言,Class虽然也是对象,但是在方法区.
    2.1 连接(验证)
  • 文件格式验证(针对二进制字节流)
  • 元数据验证(针对方法区结构)
  • 字节码验证(针对方法区结构)
  • 符号引用验证(针对方法区结构)
    2.2 连接(准备)
  • 正式为static变量分配内存,设置初始值,这些变量所使用内存在方法区中分配.
    2.3 连接(解析)
  • JVM将常量池中的符号引用替换为直接引用:符号引用(常量池的字面量), 直接引用(与JVM内存相关,有了直接引用,那么引用目标必定已经在内存中)
  • 类或者接口解析,字段解析,类方法解析,接口方法解析
    3. 初始化
    类初始化阶段是类加载过程的最后一步, 编译器为类生成的方法
    1
    2
    3
    4
    5
    <clinit>
    {
    1. 类变量赋值
    2. static{}语句块
    }
4. 类加载器

BootstrapClassLoader <— ExtensionClassLoader <— ApplicationClassLoader <—- UserClassLoader…
类层次划分, OSGI, 热部署, 代码加密…
ClassLoader只用于实现类的加载动作
对于任意一个类,都需要由它的类加载器ClassLoader和这个类本身一起确立其在JVM中的唯一性.
比较两个类是否”相等”, 即使两个类源于同一个class文件, 被同一个Jvm加载,只要加载它们的类加载器ClassLoader不同,那么这两个类必定不相等.
双亲委派模型
启动类加载器BootStrap ClassLoader, C++实现, 属于Jvm的一部分。
其他的ClassLoader, Java实现, 独立于JVM


5. 字节码执行引擎