MOOC 个人学习笔记

# 1.JVM 概述

# 虚拟机:VM, Virtual Machine

  • 逻辑上,一台虚拟的计算机
  • 实际上,一个软件,能够执行一系列虚拟的计算指令
  • 系统虚拟机
    • 对物理计算机的仿真
    • 如 VMWare,Oracle VirtualBox 等
  • 软件虚拟机
    • 专门为单个计算程序而设计
    • 如 JVM 等

# 2.JVM 内存分类

  • 线程私有内存
    • 程序计数器 (Program Counter Register)
    • Java 虚拟机栈 (JVM Stack)
    • 本地方法栈 (Native Method Stack)
  • 多线程共享内存
    • 堆 (Heap)
    • 方法区 (Method Area)
      • 运行时常量池

# 程序计数器

  • Program Counter Register, 一块小内存,每个线程都有
  • PC 存储当前方法
    • 线程正在执行的方法称为该线程的当前方法
  • 当前方法为本地 (native) 方法时,pc 值未定义 (undefined)
  • 方法为非本地方法时,pc 包含了当前正在执行指令的地址
  • 不会引发 OutOfMemoryError 异常

# JVM 栈 (JVM Stack,Java 栈)

  • 线程有自己独立的 Java 虚拟机栈,线程私有
  • -Xss 设置每个线程堆栈大小
  • Java 方法的执行基于栈
    • 每个方法从调用到完成对应一个栈帧在栈中入栈、出栈的过程
      • 栈帧存储局部变量表、操作数栈等
      • 局部变量表存放方法中存在 “栈” 里面的东西
  • 引发的异常
    • 栈的深度超过虚拟机规定深度,StackOverflowError 异常
    • 无法扩展内存,OutOfMemoryError 异常

# 本地方法栈 (Native Method Stacks)

  • 存储 native 方法的执行信息,线程私有
  • VM 规范没有对本地方法栈做明显规定
  • 引发的异常
    • 栈的深度超过虚拟机规定深度,StackOverflowError 异常
    • 无法扩展内存,OutOfMemoryError 异常

# 堆 (Heap)

  • 虚拟机启动时创建,所有线程共享,占地最大
  • 对象实例和数组都是在堆上分配内存
  • 垃圾回收的主要区域
  • 设置大小
    • -Xms 初始堆值,-Xmx 最大堆值
  • 引发的异常
    • 无法满足内存分配要求,OutOfMemoryError 异常

# 方法区 (Method Area)

  • 存储 JVM 已经加载类的结构,所有线程共享
    • 运行时常量池、类信息、常量、静态变量等
  • JVM 启动时创建,逻辑上属于堆 (Heap) 的一部分
  • 很少做垃圾回收
  • 引发的异常
    • 无法满足内存分配要求, OutOfMemoryError 异常

# 运行时常量池 (Run-Time Constant Pool)

  • Class 文件中常量池的运行时表示
  • 属于方法区的一部分
  • 动态性
    • Java 语言并不要求常量一定只有在编译期产生
    • 比如 String.intern 方法
  • 引发的异常
    • 无法满足内存分配要求,OutOfMemoryError 异常
名称线程私有 / 共享功能大小异常
程序计数器私有保存当前线程执行方法通常固定大小不会
JVM 栈私有方法的栈帧-Xss StackOverflowErrorOutOfMemoryError
本地方法栈私有存储 native 方法信息通常固定大小StackOverflowErro、OutOfMemoryError
共享存储对象和数组-Xms初始堆值-Xmx 最大堆值OutOfMemoryError
方法区共享存储类结构 / 常量 / 静态变量-XX 参数设置OutOfMemoryError
运行时常量池共享常量池运行时表示从属于方法区OutOfMemoryError

# 3.JVM 内存参数

  • 堆 (Heap)
    • 共享,内存大户,存储所有的对象和数组
    • -Xms 初始堆值,-Xmx 最大堆值
  • JVM 栈 (JVM Stack)
    • 线程私有,存储类中每个方法的内容
    • -Xss 最大栈值
  • 方法区 (Method Area)
    • 存储类信息、常量池等
    • 1.7 及以前,永久区 (Perm),-XX:PermSize, -XX:MaxPermSize
    • 1.8 及以后,元数据区,-XX:MetaspaceSize, -XX:MaxMetaspaceSize

# 4.Java 对象引用

# Java 对象的生命周期

  • 对象通过构造函数创建,但是没有析构函数回收内存
  • 对象存活在离它最近的一对大括号中

# 内存回收 API

  • Object 的 finalize 方法,垃圾收集器在回收对象时调用,有且仅被调
    用一次
  • System 的 gc 方法,运行垃圾收集器

  • 基于对象引用判定无用对象
    • 零引用,互引用等
  • 对象引用链
    • 通过一系列的称为 "GC Roots" 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说,就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的
  • GC Roots 对象包括
    • 虚拟机栈中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中引用的对象

  • 强引用
    • 例如 Object obj = new Object (); Object obj2 = obj;
    • 只要强引用还存在,对象就不会被回收,哪怕发生 OOM 异常
  • 软引用
    • 描述有用但并非必需的对象
    • 在系统将要发生内存溢出异常之前,会把这些对象列为可回收
    • JDK 提供了 SoftReference 类来实现软引用
    SoftReference<StringBuilder> s2 = new SoftReference<StringBuilder>(s1);
  • 弱引用
    • 描述非必需对象,比软引用强度更弱些
    • 被弱引用关联的对象只能生存到下一次垃圾收集发生之前
    • JDK 提供了 WeakReference 类来实现弱引用
    SoftReference<StringBuilder> s2 = new SoftReference<StringBuilder>(s1);
  • 虚引用
    • 最弱的引用关系,JDK 提供 PhantomReference 实现虚引用
    • 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知,用于对象回收跟踪
    PhantomReference<StringBuilder> s2 = new PhantomReference<StringBuilder>(s1);
引用类型强引用软引用弱引用虚引用
类型正常赋值SoftReferenceWeakReferencePhantomReference
回收时间不回收内存紧张时回收GC 就回收随时可能被回收

# 5. 垃圾收集算法

# 引用计数法

  • 一种古老的算法
  • 每个对象都有一个引用计数器
  • 有引用,计数器加一,当引用失效,计数器减一
  • 计数器为 0 的对象,将被回收
  • 优点
    • 简单,效率高
  • 缺点
    • 无法识别对象之间相互循环引用

# 标记 - 清除

  • 标记阶段:标记出所有需要回收的对象
  • 回收阶段:统一回收所有被标记的对象
  • 优点
    • 简单
  • 缺点
    • 效率不高
    • 内存碎片

# 复制算法

  • 将可用内存按容量划分为大小相等的两块,每次只使用其中的一块
  • 当这一块的内存用完了,就将还存活着的对象复制到另外一块上面
  • 然后再把已使用过的内存空间一次清理掉
  • 优点
    • 简单、高效
  • 缺点
    • 可用内存减少
    • 对象存活率高时复制操作较多

# 标记 - 整理

  • 标记阶段:与 “标记 - 清除” 算法一样
  • 整理阶段:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
  • 优点
    • 避免碎片产生
    • 无需两块相同内存
  • 缺点
    • 计算代价大,标记清除 + 碎片整理 s –更新引用地址

# 分代收集

  • Java 对象的生命周期不同,有长有短
  • 根据对象存活周期,将内存划分新生代和老年代
  • 新生代 (Young Generation)
    • 主要存放短暂生命周期的对象
    • 新创建的对象都先放入新生代,大部分新建对象在第一次 gc 时被回收
  • 老年代 (Tenured Generation)
    • 一个对象经过几次 gc 仍存活,则放入老年代
    • 这些对象可以活很长时间,或者伴随程序一生,需要常驻内存的,可以减少回收次数

# 分代收集

  • 针对各个年代特点采用合适的收集算法
    • 新生代 复制算法
    • 老年代 标记清除或标记整理

# 6.JVM 堆内存参数设置

  • -Xms 初始堆大小
  • -Xmx 最大堆大小
  • -Xmn 新生代大小
  • -XX:SurvivorRatio 设置 eden 区 /from (to) 的比例
  • -XX:NewRatio 设置老年代 / 新生代比例
  • -XX:+PrintGC/-XX:+PrintGCDetails 打印 GC 的过程信息

# HotSpot 现有垃圾收集器 (JDK 13)

  • 串行收集器 (Serial Collector)
  • 并行收集器 (Parallel Collector)
  • CMS 收集器 (Concurrent Mark Sweep Collector)
  • G1 收集器 (Garbage-First Collector)
  • Z 收集器 (Z Garbage Collector)