跟着AI学习Java,第一天

8 阅读5分钟

第 1 天学习任务

目标:JVM 核心 + 线程池基础,1.5 小时

一、学习内容

  1. JVM
  • 字节码指令:启动项目,java文件编译成字class文件,class文件就是由一个个字节码指令组成的,这些字节码,都是存在“元空间”中

  • 线程隔离区

    • 程序计数器:当前线程执行的字节码行号指示器,记录下一条字节码指令地址;线程私有,唯一不会发生 OOM 的 JVM 区域
    • 虚拟机栈:描述 Java 方法执行的内存模型,每个执行方法对应一个栈帧,栈帧操作仅入栈 / 出栈(先进后出);线程私有,会发生 StackOverflowError 和 OOM
    • 栈帧包括
      • 局部变量表:存方法参数 + 局部变量,基本类型存值,引用类型存地址
      • 操作数栈:按字节码指令入栈 / 出栈,临时存储操作数和中间结果
      • 方法出口:存调用该方法的下一条指令地址
      • 异常表:- 存异常处理信息,包含起始 / 结束 / 跳转指令地址
      • 动态链接:保存符号引用到运行时常量池直接引用的映射,调用方法 / 属性时通过编号快速获取地址
    • 本地方法栈:为 JVM 执行 Native 方法提供内存支持;线程私有,会发生 StackOverflowError 和 OOM
  • 线程共享区(所有线程共用,GC 主要回收区域)

    • :JVM 最大的内存区域,JDK7 后字符串常量池移至此区域
      • 存储内容:实例对象、数组
      • 核心特性:线程共享,会发生 OOM,是 GC(Minor GC/Full GC)的核心回收区域
    • 元空间:JDK8 及以后对方法区的实现,存储在本地内存(非 JVM 堆内存)中(JDK7 及以前方法区是永久代,属于堆内存)
      • 储存内容:- 类信息(class 文件、方法 / 属性 / 修饰符 / 字节码指令)、运行时常量池、编译代码(动态代理类)、静态变量(static 修饰)
      • 运行时常量池:存编译期生成的基本类型变量、类 / 方法的全限定名

jvm虚拟机.png

  1. 垃圾回收
  • 垃圾回收算法
算法执行步骤优点缺点适用场景
标记 - 清除标记回收对象 → 统一清除实现简单产生内存碎片极少单独使用
标记 - 复制标记存活对象 → 复制到空内存 → 清空原内存无碎片、速度快浪费一半内存空间年轻代(存活对象少)
标记 - 整理标记存活对象 → 移动至一端 → 清除边界外内存无碎片、不浪费空间效率慢老年代(存活对象多)
  • Java堆内存分代模型(堆 = 年轻代 + 老年代)
    • 方法区/元空间:类信息、常量、静态变量

    • 年轻代 Young Gen

      • 特点:对象朝生夕死,存活率极低,GC 频繁(Minor GC)、速度快
      • 内部结构:Eden 区:(新对象出生的地方) + 2 个对等的 Survivor 区(From/To)
        • **Survivor From/To 工作机制 **: Minor GC 时,将 Eden+From 区的存活对象复制到 To 区,清空 Eden+From,互换 From/To 身份;存活对象每经历 1 次 Minor GC 年龄 + 1
        • 对象晋升老年代条件
          • ① 年龄达到阈值(默认 15);
          • ② 大对象直接进入;
          • ③ Survivor 区相同年龄对象大小超过该区一半
    • 老年代 Old Gen

      • 特点:对象存活时间长,GC 频率低(Full GC)、耗时久
      • 回收算法:标记 - 整理
      • 触发老年代 GC(Full GC)常见原因:
        • 老年代空间不足
        • 元空间不足
        • 调用 System.gc ()
        • 年轻代大量对象晋升,老年代放不下
    • 分代回收的核心意义:不同对象生命周期不同,对应使用最优回收算法,最大化 GC 效率(年轻代用复制算法快,老年代用整理算法不浪费空间)

    • 常见垃圾收集器:

      • 年轻代:Serial、Parallel Scavenge、ParNew
      • 老年代:Serial Old、Parallel Old、CMS
      • 整堆回收:(JDK8 + 主流,JDK9 + 默认):G1
        • G1 核心特点:不分物理上的年轻 / 老年代,将堆划分为多个 Region;可预测停顿时间;兼顾吞吐量和延迟。
  • 常用排查命令
    • jstack(排查线程问题)

      • 作用:查看 Java 进程的线程快照,找死锁、线程卡顿、CPU 高
      • 常用命令:
        • jstack <pid>: 查看指定进程的线程信息(pid是进程号)
        • jstack <pid> | grep -i deadlock : 查找死锁(直接输出死锁信息)
    • jmap(排查内存问题)

      • 常用命令:
        • jmap -heap <pid> : 查看堆内存概况
        • jmap -dump:format=b,file=heap.hprof <pid> : 导出堆快照(生成hprof文件,用MAT分析)
    • Arthas(阿里开源,万能排查工具)

      • 作用:不用重启服务,在线排查 JVM、线程、方法调用
      • 常用命令:
        • java -jar arthas-boot.jar : 启动arthas
        • thread : 查看线程CPU占用
        • dashboard : 查看JVM信息
        • trace 类名 方法名 监控方法执行耗时
  1. 线程池
  • 为什么要用线程池
    • 避免频繁创建 / 销毁线程,降低系统开销;
    • 控制并发线程数,防止线程过多导致资源耗尽;
    • 统一管理线程,支持监控、调优、任务拒绝。
  • 线程池核心参数
    1. corePoolSize:核心线程数(默认一直存活,可通过 allowCoreThreadTimeOut (true) 设置空闲回收
    2. maximumPoolSize:线程池最大线程数(核心 + 非核心)
    3. keepAliveTime:非核心线程空闲存活时间
    4. unit:keepAliveTime 的时间单位(TimeUnit)
    5. workQueue:阻塞队列,用于存放等待执行的任务
    6. threadFactory:线程工厂,用于创建线程(自定义线程名称 / 优先级)
    7. handler:任务拒绝策略(队列满 + 线程数达最大值时触发)
  • 任务执行流程
    • 任务提交 → 核心线程未满→创建核心线程执行 → 核心线程满→放入阻塞队列 → 队列满→创建非核心线程执行 → 线程数达最大值→执行拒绝策略
  • 4 种默认拒绝策略
    • AbortPolicy:直接抛出 RejectedExecutionException(默认策略
    • DiscardPolicy:直接丢弃任务,无任何提示
    • DiscardOldestPolicy:丢弃队列中最老的任务,将新任务入队
    • CallerRunsPolicy:由提交任务的线程自己执行该任务