【Java虚拟机】JVM核心基础和常见参数实战

简介: 【Java虚拟机】JVM核心基础和常见参数实战

1.新版JVM内存组成部分和堆空间分布

JVM内存的5大组成(基于JDK8的HotSpot虚拟机,不同虚拟机不同版本会有不一样)

名称 作用 特点
程序计数器 也叫PC寄存器,用于记录当前线程执行的字节码指令位置,以便线程在恢复执行时能够从正确的位置开始 线程私有
Java虚拟机栈 用于存储Java方法执行过程中的局部变量、方法参数和返回值,以及方法执行时的操作数栈 线程私有
本地方法栈 用于存储Java程序调用本地方法的参数和返回值等信息。 线程私有
用于存储Java程序创建的对象,所有线程共享一个堆,堆中的对象可以被垃圾回收器回收,以便为新的对象分配空间 线程共享
元数据区 用于存储类的元数据信息,如类名、方法名、字段名等,以及动态生成的代理类、动态生成的字节码等
元空间是位于本地(直接)内存中的,而不是像JDK8之前方法区位于堆内存中的。
线程共享


3fcbf77c4b7040439a2a906bd28811f3.png

堆空间内存分布

  • 用于存储Java程序创建的对象,所有线程共享一个堆
  • 堆中的对象可以被垃圾回收器回收,以便为新的对象分配空间


e6a2849355f4464fbf000332c9961e12.png

2.JVM堆空间垃圾回收流程

(1)面试题:说下JVM里面堆内存划分和堆内存垃圾回收流程

  • 新建对象,放到Eden区,满后触发Minor GC(每次都是由Eden区满触发Minor GC,接连放对象到S0或S1)
  • 存活的对象移动到Survivor的S0区,如果S0满后触发Minor GC
  • S0存活下来的对象移动到S1区,然后S0区空闲
  • S1满后触发Minor GC,再次移动到S0区,然后S1区空闲
  • 反复GC每次对象涨1岁,到达一定次数后(默认15),进入老年代
  • 当老年代内存不足会触发Full GC,出现STW(Stop-The-World)
  • 堆被垃圾回收,基本都是采用分代收集算法,不通区域的采用不同的垃圾回收算法
  • 方法结束后,堆中的对象不会马上移除,在垃圾回收的时候才会被移除

(2)面试题:堆空间里面分配比例如何


5b0285d86dc64bc2971ce8057a17e4d1.png

官方推荐一般老年代与新生代的占比为2:1,即老年代占整个堆空间的2/3,新生代占整个堆空间的1/3,在Yong区又分三个区域 Eden、Survivor-0、Survivor-1,Eden分整个Yong的8/10,两个Survivor各占1/10。

3.JVM内存垃圾回收相关参数

(1)JVM参数格式分类

格式 解释 例子
标准参数(-) 所有JVM都实现这些参数的功能 -verbose:gc 打印GC简要信息
非标准参数(-X) 不保证所有JVM实现都满足 -Xmx2048m等价 -XX:MaxHeapSize JVM最大堆内存为2048M
非稳定参数(-XX) 不稳定未来可能取消,但很有用 -XX:+PrintGCDetails每次GC时打印详细信息。
-XX:+ 开启对应的参数 -XX:+PrintGCDetails 开启每次GC时打印详细信息。
-XX:- 关闭对应的参数 -XX:-DisableExplicitGC 禁止调用System.gc()
-XX:= 设定数字参数 -XX:NewRatio=2 新生代和老年代内存比例

(2)JVM堆栈内存配置参数

参数 解释
-Xms 初始堆大小,推荐和最大堆一样
-Xmx 最大堆大小,推荐和初始堆一样
-Xmn 年轻代大小
-Xss 每个线程的栈大小

(3)JVM常见的命令行参数配置

参数 解释
-XX:+PrintGCDetails 打印GC回收信息
-XX:NewRatio 新生代和老年代空间大小的比率,由-XX:NewRatio参数控制
-XX:NewRatio参数的默认值是2,表示新生代和老年代的比例是1:2
如果将-XX:NewRatio设置为4,表示新生代和老年代的比例是1:4
-XX:MaxMetaspaceSize 元空间所分配内存的最大值,默认没限制
-XX:+UseConcMarkSweepGC 设置并发收集器

4.JVM虚拟机栈参数调整案例实战

JVM虚拟机栈

  • 用来存储Java程序中的方法调用和局部变量的内存区域
  • 每个线程都有自己的虚拟机栈,其生命周期与线程相同
  • 当一个方法被调用时,Java虚拟机会在该线程的虚拟机栈中创建一个栈帧,用来存储该方法的局部变量、方法返回值等信息
  • 异常情况
  • 默认情况下,JVM虚拟机栈的大小是固定的,JDK1.5后通常为1MB
  • 如果线程在执行方法时需要更多的栈空间,JVM会抛出StackOverflowError异常
  • JVM参数 xss,比如 -Xss1m 表示1MB

(1)案例:模拟递归调用,对count一直++,直到栈溢出

public class StackFrameDemo {
    private static int count = 0;
    public static void main(String[] args) {
        try {
            recursiveMethod();
        } catch (Throwable t) {
            System.out.println("Stack overflow after " + count + " invocations.");
            t.printStackTrace();
        }
    }
    private static void recursiveMethod() {
        count++;
        recursiveMethod();
    }
}
  • 配置栈大小,最少208k,低于208k启动不起来项目,我们这块配置 524k,-Xss524k

6a5c047398e446acaaaf280912b6e4c1.png

  • 再次测试


53d60b8a152e4d4291db6f1ca5438d44.png

  • 结论:
  • 栈越小,递归调用的次数就越少,因为栈空间不足导致栈溢出异常
  • 栈越大,递归调用的次数就越多,因为有足够的栈空间来存储方法调用的信息

5.JVM堆参数调整压测案例实战

  • 需求
  • 通过调整不同的JVM堆参数,查看相关指标
  • 测试接口
@RestController
@RequestMapping("api/v1/data")
public class DataController {
    @RequestMapping("compute")
    public String compute() {
        Byte[] b = new Byte[1024*1024];
        return "success";
    }
}
  • JVM参数
  • 调整参数一
  • 参数 -Xms64m -Xmx64m
  • 性能指标


f054390041cb4bd49821ae89ddbe8279.png

  • 调整参数二
  • 参数 -Xms640m -Xmx640m
  • 性能指标

29d27b4142e047368d29e2434b3a7b86.png

6.JDK8之后的方法区实现和元空间的联系

(1)什么是方法区和元空间

  • 【方法区】是JVM中用来存储类的元数据信息的区域,包括类的结构、方法、字段信息等,Java堆类似各个线程共享的内存区域
  • 元空间、永久代是方法区具体的落地实现
  • java8之前是称为永久代(PermGen),java8后引入的一个新概念【元空间】用于替代旧版JVM中的永久代(PermGen)
  • 方法区和永久代以及元空间的关系很像 Java 中接口和类的关系
  • 类实现了接口,类就可以看作是永久代和元空间,接口可以看作是方法区
  • 永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现便成为元空间


204177e1bfbb45c49bb470d7e3d1e789.png

  • 元空间的大小是动态的,可以根据需要进行自动扩展,如果元空间不足,JVM会抛出 OutOfMemoryError : Metaspace
  • 元空间大小配置
  • -XX:MetaspaceSize
  • 用来设置元空间初始大小的参数,它的默认值是21 MB
  • -XX:MaxMetaspaceSize
  • 用来设置元空间最大大小的参数,它的默认值是-1 即不限制,使用的是本地内存,不像旧版的永久代是堆内存
  • 如果不限制元空间的大小,可能会导致元空间占用过多的内存,从而引起内存溢出
  • 系统参数查看
  • 这两个参数的单位是字节(B),可以使用K、M、G等后缀来表示更大的单位
public class HeapDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("测试元空间进程");
        Thread.sleep(10000000);
    }
}
  • 查看命令
jps #查看进程号
jinfo -flag MetaspaceSize 进程号  #查看Metaspace分配内存空间 
jinfo -flag MaxMetaspaceSize 进程号 #查看Metaspace最大空间

5719073af21a4cacb943d4a7120efd6b.png

  • 调整
-XX:MetaspaceSize=126m -XX:MaxMetaspaceSize=524m

8c1c5d2bee694fcdb70c59510929fafa.png


相关文章
|
28天前
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
175 3
|
30天前
|
缓存 前端开发 Java
基于最新 Java 技术栈的在线任务管理系统开发实战详解
本项目基于最新Java技术栈开发在线任务管理系统,涵盖任务创建、分配、跟踪、统计等功能。采用Spring Boot 3.2.x、React 18、PostgreSQL 16等主流技术,详解项目架构设计、核心功能实现及部署流程,助力掌握现代Java全栈开发技能。
117 6
|
28天前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
276 1
|
1月前
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
136 7
|
1月前
|
消息中间件 Java 数据库
Java 基于 DDD 分层架构实战从基础到精通最新实操全流程指南
本文详解基于Java的领域驱动设计(DDD)分层架构实战,结合Spring Boot 3.x、Spring Data JPA 3.x等最新技术栈,通过电商订单系统案例展示如何构建清晰、可维护的微服务架构。内容涵盖项目结构设计、各层实现细节及关键技术点,助力开发者掌握DDD在复杂业务系统中的应用。
271 0
|
2月前
|
人工智能 Cloud Native Java
2025 年 Java 应届生斩获高薪需掌握的技术实操指南与实战要点解析
本指南为2025年Java应届生打造,涵盖JVM调优、响应式编程、云原生、微服务、实时计算与AI部署等前沿技术,结合电商、数据处理等真实场景,提供可落地的技术实操方案,助力掌握高薪开发技能。
143 2
|
2月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
67 0
|
2月前
|
安全 Java 网络安全
Java 实现 SMTP 协议调用的详细示例及实战指南 SMTP Java 调用示例
本文介绍了如何使用Java调用SMTP协议发送邮件,涵盖SMTP基本概念、JavaMail API配置、代码实现及注意事项,适合Java开发者快速掌握邮件发送功能集成。
211 0
|
2月前
|
缓存 Java API
Java 面试实操指南与最新技术结合的实战攻略
本指南涵盖Java 17+新特性、Spring Boot 3微服务、响应式编程、容器化部署与数据缓存实操,结合代码案例解析高频面试技术点,助你掌握最新Java技术栈,提升实战能力,轻松应对Java中高级岗位面试。
311 0