【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


相关文章
|
9天前
|
存储 监控 Java
JVM实战—8.如何分析jstat统计来定位GC
本文详细介绍了使用jstat、jmap和jhat等工具分析JVM运行状况的方法,以及如何合理优化JVM性能。内容涵盖新生代与老年代对象增长速率、Young GC和Full GC的触发频率及耗时等关键指标的分析。通过模拟BI系统和计算系统的案例,展示了如何根据实际场景调整JVM参数以减少FGC频率,提升系统性能。最后汇总了常见问题及其解决方案,帮助开发者更好地理解和优化JVM运行状态。
JVM实战—8.如何分析jstat统计来定位GC
|
8天前
|
缓存 监控 算法
JVM实战—10.MAT的使用和JVM优化总结
本文详细探讨了JVM内存管理与性能优化的关键问题。首先分析了线上大促活动引发的老年代内存泄漏及频繁FGC问题,通过MAT工具定位到本地缓存未正确处理的原因,并提出使用Ehcache等框架解决。接着讨论了百万级数据误处理导致的频繁FGC案例,深入剖析String.split()方法在特定JDK版本下的内存消耗问题,并给出多线程并发处理大数据量的优化建议。文章还总结了JVM运行原理、GC机制以及YGC和FGC的触发条件,明确了正常系统GC频率指标。最后提供了JVM性能优化的整体思路,包括新系统开发时的参数预估、压测后的调整策略以及线上系统的监控方法,同时列举了常见的FGC原因及对应解决方案。
130 79
JVM实战—10.MAT的使用和JVM优化总结
|
7天前
|
消息中间件 缓存 Java
JVM实战—11.OOM的原因和模拟以及案例
本文详细探讨了Java系统中内存溢出(OutOfMemory,简称OOM)问题的成因与解决方法。首先分析了线上系统因OOM挂掉的常见场景及处理思路,接着深入讲解了JVM中可能发生OOM的三大区域:Metaspace(类信息存储区)、栈内存(线程执行方法时使用)和堆内存(对象存储区)。针对每个区域,文章通过具体代码示例模拟了内存溢出的情况,如动态生成过多类导致Metaspace溢出、无限递归调用引发栈内存溢出以及高负载下堆内存不足等问题。最后结合实际案例,如大数据处理系统因Kafka故障未正确处理数据缓存而导致OOM,以及无限循环调用或未缓存动态代理类引发的问题,给出了预防和改进措施。
130 64
JVM实战—11.OOM的原因和模拟以及案例
|
4天前
|
SQL 前端开发 Java
JVM实战—13.OOM的生产案例
本文详细探讨了多种线上系统中引发OOM(内存溢出)问题的原因及排查方法。内容涵盖:1)每秒仅上百请求的系统因RPC超时时间设置过长导致QPS激增而OOM;2)Jetty服务器NIO机制因堆外内存管理不当引发内存溢出;3)微服务架构下RPC调用因类定义不一致导致超大byte[]数组占用内存;4)SQL语句缺少WHERE条件查询大量数据引发OOM;5)日志分析系统因堆内存不足与递归操作耗尽内存;6)类加载器过多导致内存使用过高被OS杀死进程;7)数据同步系统频繁OOM的排查与解决;8)总结JVM参数优化、GC问题定位及OOM分析方法。
JVM实战—13.OOM的生产案例
|
9天前
|
SQL 缓存 监控
JVM实战—9.线上FGC的几种案例
本文详细探讨了JVM性能优化中的几个关键案例与问题。首先分析了如何优化每秒十万QPS的社交APP,通过增加Survivor区大小和优化内存碎片解决频繁Full GC的问题。接着讨论了垂直电商后台系统FGC的深度优化,定制JVM参数模板以降低GC频率。还探讨了不合理设置JVM参数导致频繁FGC的情况,并提出了解决方案。此外,针对线上系统每天数十次FGC的问题,定位到大对象是主要原因,并通过调整新生代大小等参数优化。同时,分析了电商大促活动中因System.gc()调用导致系统卡死的现象,建议禁用显式GC。
JVM实战—9.线上FGC的几种案例
|
6天前
|
缓存 监控 Java
JVM实战—12.OOM的定位和解决
本文详细探讨了JVM内存管理中的常见问题及其解决方案,包括如何监控和报警系统的OOM异常、在内存溢出时自动Dump内存快照、解决Metaspace区域内存溢出、栈内存溢出(StackOverflowError)以及堆内存溢出(OutOfMemoryError: Java heap space)。针对每种情况,文章提供了具体的解决思路、示例代码、GC日志分析及内存快照分析方法。通过搭建系统监控体系、调整JVM参数和使用工具如MAT,可以有效定位和解决各类内存问题,优化系统性能并避免崩溃风险。
JVM实战—12.OOM的定位和解决
|
2天前
|
Java Linux 定位技术
Minecraft配置文件参数说明(JAVA服务器篇)
Minecraft JAVA版服务器启动后会生成server.properties配置文件,位于minecraft_server/根目录下。该文件包含多项关键设置,如游戏模式(gamemode)、最大玩家数(max-players)、难度(difficulty)等。此文档详细说明了各配置项的功能与默认值,帮助用户高效管理服务器环境。
44 3
|
8天前
|
机器学习/深度学习 人工智能 Java
Java机器学习实战:基于DJL框架的手写数字识别全解析
在人工智能蓬勃发展的今天,Python凭借丰富的生态库(如TensorFlow、PyTorch)成为AI开发的首选语言。但Java作为企业级应用的基石,其在生产环境部署、性能优化和工程化方面的优势不容忽视。DJL(Deep Java Library)的出现完美填补了Java在深度学习领域的空白,它提供了一套统一的API,允许开发者无缝对接主流深度学习框架,将AI模型高效部署到Java生态中。本文将通过手写数字识别的完整流程,深入解析DJL框架的核心机制与应用实践。
29 2
|
8月前
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
92 0
|
8月前
|
存储 Java 程序员
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
122 10