当前位置: 首页 > news >正文

《深入理解Java虚拟机》学习笔记

一、Java虚拟机(JVM)概述

  1. 定义与功能
    • 作为Java程序运行的中间层,将字节码转换为机器码,实现跨平台运行。
    • 核心功能:类加载、内存管理、垃圾回收、字节码执行、运行时优化。
  2. JVM架构组件
    • 类加载器子系统:加载、验证、解析类文件。
    • 执行引擎:解释器、JIT编译器、垃圾收集器。
    • 本地接口:调用本地方法库(如Native方法)。
  3. 版本与特性演变
    • 不同JDK版本(JDK7、JDK8、JDK11等)在内存模型、垃圾收集器上的重大变化(如永久代→元空间,G1成为默认GC)。

二、Java内存区域与内存溢出

  1. 运行时数据区域(按线程共享/隔离划分)
    • 线程共享区域:
      • 堆(Heap):
        • 存储所有对象实例和数组。
        • 分为新生代(Eden区、From/To Survivor区)和老年代。
        • 溢出场景:内存泄漏、大对象分配失败。
      • 方法区(Method Area):
        • 存储类信息(类结构、常量池、静态变量)。
        • JDK7前:永久代(PermGen),易因动态加载类导致溢出。
        • JDK8+:元空间(Metaspace),使用本地内存,减少永久代问题。
      • 运行时常量池:
        • 存放字面量(如字符串常量)、符号引用(类/方法名)。
    • 线程隔离区域:
      • 程序计数器(PC Register):
        • 记录线程当前执行指令地址,线程切换后恢复执行位置。
      • 虚拟机栈(Java Stack):
        • 每个方法对应一个栈帧,存储局部变量、操作数栈、方法调用信息。
        • 溢出场景:递归过深、方法调用栈过深(StackOverflowError)。
      • 本地方法栈(Native Method Stack):
        • 为Native方法服务,溢出处理类似Java栈。
    • 直接内存(Direct Memory):
      • 通过NIO分配,不受堆限制,但需手动管理,溢出导致OOM。
  2. 内存溢出异常(OOM)分析
    • 常见原因:
      • 堆:对象创建过多且无法回收(内存泄漏)。
      • 栈:递归/线程过多导致栈深度超过限制。
      • 方法区:动态生成大量类(如动态代理、反射)。
      • 直接内存:NIO操作超出系统限制。
    • 排查工具:jmap、MAT(Memory Analyzer)分析堆dump,jstack分析线程栈。

三、垃圾回收机制

  1. 垃圾回收目标:识别并回收“不可达对象”(通过可达性分析算法)。
  2. 可达性分析:
    • GC Roots:虚拟机栈引用、静态变量、常量、本地方法栈引用、JNI引用。
  3. 垃圾回收算法:
    • 标记-清除(Mark-Sweep):
      • 标记存活对象,清除未标记对象,易产生内存碎片。
    • 复制(Copying):
      • 将内存分为两块,存活对象复制到另一块,适用于新生代(如Eden→Survivor)。
    • 标记-整理(Mark-Compact):
      • 标记后移动存活对象到一端,清理剩余空间,减少碎片,常用于老年代。
    • 分代收集:
      • 新生代:复制算法(高存活率低,快速回收)。
      • 老年代:标记-整理或标记-清除(低存活率,需处理碎片)。
  4. 垃圾收集器:
    • Serial/Serial Old:单线程,适合内存小、响应要求低的场景。
    • Parallel Scavenge/Parallel Old:多线程,关注吞吐量(后台任务)。
    • CMS(Concurrent Mark Sweep):
      • 低停顿,但并发阶段CPU消耗高,易产生碎片(需Full GC清理)。
    • G1(Garbage First):
      • 分区域管理,优先回收高价值区域,兼顾延迟与吞吐量,适合大堆内存。
    • ZGC(Z Garbage Collector):
      • JDK11引入,低延迟(<10ms),支持TB级堆内存。
  5. 垃圾回收触发时机:
    • Minor GC:新生代满(Eden区分配不足)。
    • Major GC/Full GC:老年代满、永久代/元空间满、System.gc()触发。
  6. 引用类型:
    • 强引用:普通引用,不回收。
    • 软引用:内存不足时回收(如缓存)。
    • 弱引用:下次GC时回收。
    • 虚引用:仅用于跟踪回收事件(配合引用队列使用)。

四、类加载机制

  1. 类加载过程:
    • 加载:获取字节码,生成Class对象。
    • 验证:字节码格式、元数据、字节码合法性检查。
    • 准备:为静态变量分配内存,初始化默认值(如int=0)。
    • 解析:将符号引用转为直接引用(如方法/字段地址)。
    • 初始化:执行静态代码块,初始化静态变量为实际值。
  2. 类加载器:
    • 双亲委派模型:
      • 加载类时先委托父类加载器,防止核心类被篡改(如自定义java.lang.Object)。
    • 加载器类型:
      • 启动类加载器(Bootstrap):加载核心类库。
      • 扩展类加载器(Ext):加载扩展库($JAVA_HOME/lib/ext)。
      • 应用类加载器(App):加载应用类路径。
      • 自定义加载器:用于热部署、加密类等场景。
  3. 类加载器破坏场景:
    • 线程上下文加载器(Thread Context ClassLoader):打破双亲委派,如SPI机制。
    • 动态加载(如OSGI、模块化系统)的类加载器自定义策略。

五、字节码执行与即时编译(JIT)

  1. 字节码执行引擎:
    • 解释执行:逐条解释字节码,速度慢。
    • 即时编译(JIT):将热点代码(如循环、高频方法)编译为本地机器码,提升性能。
  2. JIT优化技术:
    • 逃逸分析:判断对象是否逃逸到方法外,优化内存分配(如栈上分配)。
    • 锁消除:去除不必要的同步代码。
    • 标量替换:将对象拆分为基本类型,减少堆分配。
  3. 分层编译:
    • 从解释执行→C1编译(简单优化)→C2编译(高级优化),逐步提升效率。
  4. 热点探测:
    • 基于计数器(方法调用次数、循环次数)触发编译阈值。

六、性能优化与故障排查

  1. 性能调优工具:
    • 监控工具:jstat(GC统计)、jmap(堆快照)、jstack(线程栈)、VisualVM、Arthas。
    • 日志分析:GC日志(-XX:+PrintGCDetails)、堆dump(-XX:HeapDumpOnOutOfMemoryError)。
  2. 调优参数:
    • 内存设置:-Xms(初始堆)、-Xmx(最大堆)、-XX:MetaspaceSize(元空间)。
    • GC选择:-XX:+UseG1GC-XX:ParallelGCThreads
    • 日志与调试:-XX:+PrintFlagsFinal(查看参数)、-XX:+HeapDumpOnCtrlBreak
  3. 优化策略:
    • 减少Full GC:调整新生代/老年代比例,避免大对象直接进入老年代。
    • 代码优化:减少临时对象创建、使用对象池、避免频繁的装箱/拆箱。
    • 并发优化:选择合适的垃圾收集器(如G1 vs CMS)。

七、Java内存模型与并发处理

  1. 内存模型(JMM):
    • 定义多线程访问共享变量的规则,解决可见性、有序性问题。
    • 核心概念:主内存(共享变量存储)、工作内存(线程私有缓存)。
  2. 同步机制:
    • synchronized:保证原子性、可见性,通过锁机制实现。
    • volatile:禁止指令重排序,确保变量修改立即同步到主内存。
  3. 锁优化:
    • 偏向锁:单线程场景优化,减少锁竞争。
    • 轻量级锁:无竞争时自旋等待。
    • 重量级锁:锁竞争激烈时升级,阻塞线程。
  4. 原子性操作:使用AtomicXXX类(如AtomicInteger)实现无锁并发。

八、高效并发的实现原理

  1. JVM内存模型与线程:
    • 每个线程有自己的栈和程序计数器,共享堆和方法区。
  2. 线程安全与可见性:
    • 先行发生原则(Happens-Before):定义操作之间的顺序关系。
  3. 线程池配置:
    • 参数调优:核心线程数、最大线程数、队列大小。
    • 监控线程状态(如死锁、阻塞)。

九、其他高级特性

  1. 字节码与Class文件结构:
    • Class文件格式(魔数、版本号、常量池、字段表、方法表)。
    • 字节码指令集(如aload、invokevirtual)。
  2. 编译与优化:
    • 语法糖:泛型擦除、自动装箱/拆箱、条件编译。
    • 即时编译触发条件与优化层级。
  3. JVM故障排查:
    • CPU占用高:使用jstack定位线程状态(如死循环)。
    • 内存泄漏:MAT分析对象引用链。
    • 线程死锁:jconsole或jstack检测死锁线程。

十、实战建议与最佳实践

  1. 内存溢出排查流程:
    • 捕获OOM异常→生成堆dump→分析大对象/引用链→优化代码或配置。
  2. 调优实践:
    • 压测工具(如JMeter)模拟负载,结合GC日志调整参数。
    • 使用JVM参数模板(如微服务场景的G1配置)。
  3. 代码优化示例:
    • 使用StringBuilder代替+拼接字符串。
    • 避免在循环中创建对象。

总结:

理解JVM底层机制是解决性能问题、优化代码、排查故障的基础。需结合具体场景(如高并发、大数据处理)灵活应用调优策略,并关注JDK新版本特性(如JDK17的垃圾收集器改进)。


参考资料:

  • 《深入理解Java虚拟机》(周志明著,第三版)
  • Oracle官方JVM文档、OpenJDK源码
http://www.dtcms.com/a/349024.html

相关文章:

  • 【高级】系统架构师 | 系统工程
  • Java面试篇
  • 数字防线:现代企业网络安全运维实战指南
  • WinContig:高效磁盘碎片整理工具
  • [Mysql数据库] 知识点总结1
  • [身份验证脚手架] docs | breeze:install
  • 电梯间电动车误检率↓79%!陌讯多模态融合算法实战解析
  • ROS1中的Package
  • Ansible 自动化基石:变量定义、优先级控制与 Vault 敏感信息加密实战指南
  • 100个实用小工具1.3历年股价分析小工具新增股价批量下载
  • Linux shell脚本条件循环
  • MATLAB 在工程仿真中的实践:以机械振动分析为例的完整流程
  • 1.Spring Boot:超越配置地狱,重塑Java开发体验
  • LeetCode 101 刷题 - (2) 第二章 玩转双指针
  • Jupyter Lab 常用快捷键清单
  • C++标准库头文件使用指南
  • 【C++】10. list
  • BUCK电路的环路补偿
  • JDK版本报错
  • 在PC机上使用虚幻引擎5(UE5)开发第一款游戏的完整入门指南
  • 门面设计模式
  • Python核心技术开发指南(012)——浮点数
  • 如何捕获组件的异常情况
  • 一个简单的html音乐播放器
  • 阿里发布Qoder:颠覆软件开发体验的AI编程平台
  • 前端应用容器化,基于Docker多阶段构建的最佳实践
  • More Effective C++ 条款05: 谨慎定义类型转换函数
  • Java 泛型的“擦除”与“保留”:一次完整的编译与反编译实验
  • Docker中Dify镜像由Windows系统迁移到Linux系统的方法
  • 【计算机408数据结构】第二章:基本数据结构之线性表