# Java虚拟机

# JVM内存模型

# 运行时数据区

  1. 程序计数器

    • 当前线程执行字节码的行号指示器
    • 线程私有
    • 唯一一个不会发生OutOfMemoryError的内存区域
  2. Java虚拟机栈

    • 线程私有
    • 存储局部变量表、操作数栈、动态链接、方法出口等信息
    • 可能抛出StackOverflowError和OutOfMemoryError
  3. 本地方法栈

    • 线程私有
    • 为Native方法服务
    • 可能抛出StackOverflowError和OutOfMemoryError
  4. Java堆

    • 线程共享
    • 存放对象实例
    • 垃圾收集器管理的主要区域
    • 可能抛出OutOfMemoryError
  5. 方法区

    • 线程共享
    • 存储已被虚拟机加载的类信息、常量、静态变量等
    • JDK8后被元空间取代

# 对象创建过程

  1. 类加载检查

    • 检查类是否已加载
    • 如未加载,执行类加载过程
  2. 分配内存

    • 指针碰撞
    • 空闲列表
    • TLAB(Thread Local Allocation Buffer)
  3. 初始化零值

    • 保证对象实例字段在Java代码中可以不赋初值就直接使用
  4. 设置对象头

    • 存储对象运行时数据(HashCode、GC分代年龄等)
    • 类型指针(指向方法区中的类元数据)
  5. 执行init方法

    • 按照程序员的意愿进行初始化

# 垃圾回收

# 垃圾判断算法

  1. 引用计数法

    • 给对象添加一个引用计数器
    • 优点:实现简单,判定效率高
    • 缺点:无法解决循环引用问题
  2. 可达性分析

    • 从GC Roots开始搜索
    • 不可达的对象被认为是可回收的
    • GC Roots包括:
      • 虚拟机栈中引用的对象
      • 方法区中静态属性引用的对象
      • 方法区中常量引用的对象
      • 本地方法栈中JNI引用的对象

# 垃圾收集算法

  1. 标记-清除算法

    • 标记阶段:标记出所有需要回收的对象
    • 清除阶段:回收被标记的对象
    • 缺点:效率低,产生大量内存碎片
  2. 复制算法

    • 将内存分为两块
    • 存活对象复制到另一块
    • 优点:效率高,无内存碎片
    • 缺点:内存利用率低
  3. 标记-整理算法

    • 标记阶段:标记存活对象
    • 整理阶段:将存活对象向一端移动
    • 优点:无内存碎片
    • 缺点:移动对象成本高
  4. 分代收集算法

    • 新生代:复制算法
    • 老年代:标记-清除或标记-整理算法

# 垃圾收集器

  1. Serial收集器

    • 单线程收集器
    • 简单高效
    • 适用于客户端环境
  2. ParNew收集器

    • Serial收集器的多线程版本
    • 常与CMS收集器配合使用
  3. Parallel Scavenge收集器

    • 新生代收集器
    • 关注吞吐量
    • 可控制的吞吐量
  4. CMS收集器

    • 以获取最短回收停顿时间为目标
    • 基于标记-清除算法
    • 过程:
      • 初始标记
      • 并发标记
      • 重新标记
      • 并发清除
  5. G1收集器

    • 面向服务端应用的收集器
    • 可预测的停顿时间模型
    • 特点:
      • 并行与并发
      • 分代收集
      • 空间整合
      • 可预测的停顿

# 类加载机制

# 类加载过程

  1. 加载

    • 通过类的全限定名获取二进制字节流
    • 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
    • 在内存中生成Class对象
  2. 验证

    • 文件格式验证
    • 元数据验证
    • 字节码验证
    • 符号引用验证
  3. 准备

    • 为类变量分配内存
    • 设置初始值
  4. 解析

    • 将符号引用替换为直接引用
    • 解析的对象:
      • 类或接口
      • 字段
      • 类方法
      • 接口方法
  5. 初始化

    • 执行类构造器<clinit>()方法
    • 父类优先于子类初始化

# 类加载器

  1. Bootstrap ClassLoader

    • 加载Java核心类库
    • 使用C++实现
    • 是所有类加载器的父类
  2. Extension ClassLoader

    • 加载扩展类库
    • 位于jre/lib/ext目录
  3. Application ClassLoader

    • 加载应用程序classpath目录下的类
  4. 自定义类加载器

    • 继承ClassLoader类
    • 重写findClass方法

# 双亲委派模型

  1. 工作原理

    • 收到类加载请求时,先将请求委派给父加载器
    • 父加载器无法完成加载时,子加载器才会尝试加载
  2. 优点

    • 避免类的重复加载
    • 保证Java核心类库的安全性
  3. 破坏双亲委派模型

    • 线程上下文类加载器
    • OSGi模块化框架

# JVM调优

# 调优工具

  1. jps

    • 显示Java进程状态
    • 常用选项:
      • -l:输出主类全名
      • -v:输出JVM参数
      • -m:输出主类参数
  2. jstat

    • 监视虚拟机运行状态
    • 常用选项:
      • -gcutil:监视内存和GC情况
      • -class:监视类加载情况
      jstat -gcutil <pid> <interval> <count>
      
  3. jmap

    • 生成堆转储快照
    • 查看堆内存使用状况
    • 常用选项:
      • -heap:显示堆详细信息
      • -dump:生成堆转储文件
      jmap -dump:format=b,file=heap.hprof <pid>
      
  4. jstack

    • 生成线程快照
    • 分析线程状态和死锁
    • 示例:
      jstack -l <pid>
      

# 性能监控

  1. 内存监控

    public class MemoryMonitor {
        public static void printMemoryInfo() {
            Runtime runtime = Runtime.getRuntime();
            long totalMemory = runtime.totalMemory();
            long freeMemory = runtime.freeMemory();
            long maxMemory = runtime.maxMemory();
            
            System.out.println("Total Memory: " + totalMemory / 1024 / 1024 + "MB");
            System.out.println("Free Memory: " + freeMemory / 1024 / 1024 + "MB");
            System.out.println("Max Memory: " + maxMemory / 1024 / 1024 + "MB");
        }
        
        public static void monitorMemoryUsage() {
            MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            
            System.out.println("Heap Memory Usage:");
            System.out.println("Init: " + heapUsage.getInit() / 1024 / 1024 + "MB");
            System.out.println("Used: " + heapUsage.getUsed() / 1024 / 1024 + "MB");
            System.out.println("Committed: " + heapUsage.getCommitted() / 1024 / 1024 + "MB");
            System.out.println("Max: " + heapUsage.getMax() / 1024 / 1024 + "MB");
        }
    }
    
  2. GC监控

    public class GCMonitor {
        public static void attachGCMonitor() {
            NotificationEmitter emitter = (NotificationEmitter) ManagementFactory.getGarbageCollectorMXBeans().get(0);
            
            emitter.addNotificationListener((notification, handback) -> {
                if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
                    GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
                    
                    String gcName = info.getGcName();
                    String gcAction = info.getGcAction();
                    String gcCause = info.getGcCause();
                    long duration = info.getGcInfo().getDuration();
                    
                    System.out.println("GC Event:");
                    System.out.println("Name: " + gcName);
                    System.out.println("Action: " + gcAction);
                    System.out.println("Cause: " + gcCause);
                    System.out.println("Duration: " + duration + "ms");
                }
            }, null, null);
        }
    }
    

# 调优实战

  1. 内存泄漏排查

    • 使用jmap生成堆转储文件
    • 使用MAT等工具分析堆转储文件
    • 定位可能的内存泄漏点
    public class MemoryLeakDemo {
        private static List<byte[]> list = new ArrayList<>();
        
        public static void potentialLeak() {
            while (true) {
                list.add(new byte[1024 * 1024]); // 1MB
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  2. GC调优

    • 分析GC日志
    • 调整垃圾收集器参数
    • 优化内存分配
    public class GCLogAnalyzer {
        public static void analyzeGCLog(String logPath) {
            try (BufferedReader reader = new BufferedReader(new FileReader(logPath))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (line.contains("[GC")) {
                        parseGCLog(line);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        private static void parseGCLog(String log) {
            // 解析GC日志的具体实现
            // 1. 提取GC类型
            // 2. 分析内存变化
            // 3. 统计GC时间
        }
    }
    
  3. 线程优化

    • 分析线程栈
    • 解决线程死锁
    • 优化线程池参数
    public class ThreadAnalyzer {
        public static void detectDeadlock() {
            ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
            long[] deadlockedThreads = threadBean.findDeadlockedThreads();
            
            if (deadlockedThreads != null) {
                ThreadInfo[] threadInfo = threadBean.getThreadInfo(deadlockedThreads);
                System.out.println("Deadlock detected:");
                for (ThreadInfo info : threadInfo) {
                    System.out.println(info.getThreadName() + " locked on " + 
                        info.getLockName() + " owned by " + info.getLockOwnerName());
                }
            }
        }
    }
    
  4. 性能优化建议

    • 合理设置堆内存大小
    • 选择适合的垃圾收集器
    • 避免频繁Full GC
    • 及时解决内存泄漏
    • 优化代码结构

# 最佳实践

  1. JVM参数配置

    # 生产环境配置示例
    JAVA_OPTS="
        -Xms4g 
        -Xmx4g 
        -XX:MetaspaceSize=256m 
        -XX:MaxMetaspaceSize=256m 
        -XX:+UseG1GC 
        -XX:MaxGCPauseMillis=100 
        -XX:+HeapDumpOnOutOfMemoryError 
        -XX:HeapDumpPath=/path/to/dump.hprof 
        -Xlog:gc*:file=/path/to/gc.log:time,uptime:filecount=5,filesize=100m"
    
  2. 性能监控实现

    public class PerformanceMonitor {
        private static final ScheduledExecutorService scheduler = 
            Executors.newScheduledThreadPool(1);
        
        public static void startMonitoring() {
            scheduler.scheduleAtFixedRate(() -> {
                System.out.println("=== Performance Statistics ===");
                printMemoryStats();
                printThreadStats();
                printGCStats();
            }, 0, 1, TimeUnit.MINUTES);
        }
        
        private static void printMemoryStats() {
            Runtime runtime = Runtime.getRuntime();
            long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
            System.out.println("Used Memory: " + usedMemory + "MB");
        }
        
        private static void printThreadStats() {
            ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
            System.out.println("Active Threads: " + threadBean.getThreadCount());
            System.out.println("Peak Threads: " + threadBean.getPeakThreadCount());
        }
        
        private static void printGCStats() {
            for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
                System.out.println(gcBean.getName() + " - Count: " + 
                    gcBean.getCollectionCount() + ", Time: " + 
                    gcBean.getCollectionTime() + "ms");
            }
        }
    }
    
  3. 调优工具使用

    public class TuningToolsDemo {
        public static void main(String[] args) {
            // 1. 启动性能监控
            PerformanceMonitor.startMonitoring();
            
            // 2. 添加GC监听
            GCMonitor.attachGCMonitor();
            
            // 3. 定期检查死锁
            ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
            scheduler.scheduleAtFixedRate(() -> {
                ThreadAnalyzer.detectDeadlock();
            }, 0, 5, TimeUnit.MINUTES);
            
            // 4. 模拟业务逻辑
            runBusinessLogic();
        }
        
        private static void runBusinessLogic() {
            // 业务代码
        }
    }
    

这些工具和示例代码可以帮助开发者更好地监控和优化JVM性能。在实际使用中,需要根据具体的应用场景和性能需求来选择合适的监控指标和优化策略。