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

jvm参数调优(持续更新)

JVM虚拟机

  • 调试参数
  • cpu占用过高
  • 运行调试工具
  • 方法区
  • 常量池
    • StringTable性能调优
  • 直接内存 DirectMemory

调试参数

更多jvm参数配置跳转

  • -Xss {数字} 调整栈的运行大小
  • -Xms(Initial Heap Size)
    作用:设置 JVM 初始堆内存大小(程序启动时分配的堆内存)。
    默认值:物理内存的 1/64(例如,8GB 内存的机器默认初始堆约为 128MB)。
    示例:-Xms512m 表示初始堆大小为 512MB。
  • -Xmx(Maximum Heap Size)
    作用:设置 JVM 最大堆内存大小(程序运行期间堆内存的上限)。
    默认值:物理内存的 1/4(例如,8GB 机器默认最大堆约为 2GB)。
    示例:-Xmx2g 表示最大堆大小为 2GB。
    -XX:+PrintStringTableStatistics 是 Java 虚拟机(JVM)中的一个非稳定参数(-XX 参数),用于在 JVM 终止时打印字符串常量池(String Pool)的统计信息,包括哈希表桶的数量、已使用的桶数量、字符串数量等。
  • -XX:StringTableSize 是 Java 虚拟机(JVM)中的一个非稳定参数(-XX 参数),用于设置字符串常量池(String Pool)的哈希表桶数量,旨在优化字符串的存储和查找效率,减少哈希冲突,从而提升性能。使用方法:-XX:StringTableSize = {N}

cpu占用过高

  • 当电脑程序占用cpu过高,也许是某些程序线程有问题,如何找到是哪个线程在作怪?
  • top 是一个实时动态查看系统资源占用和进程状态的命令行工具,广泛用于 Linux/Unix 系统监控和性能分析。它能以全屏交互式界面显示关键系统指标(如 CPU、内存、负载等)和所有正在运行的进程的详细信息,并支持按需排序和过滤。
  1. 使用top指令查看占用cpu较多的进程
  2. ps H -eo pid,tid,%cpu |grep 某个程序pid 就可以查看该进程线程占用cpu比率
  • jstack 是 Java 虚拟机(JVM)提供的命令行工具,用于生成当前 JVM 中所有线程的堆栈跟踪信息(Thread Dump)。它能帮助开发者快速诊断线程状态、死锁、阻塞、高 CPU 占用等问题。可以显示一个java进程中所有线程的信息,比如nid–>操作系统级别的线程ID,nid是以十六机制表示的,通过显示信息可以定位到问题代码行数

使用方法: jstack {pid}     显示的nid是十六进制,需要执行转换

运行调试工具

  • jsp 查看当前运行的java进程pid
  • jmap 是 JDK 自带的命令行工具,用于生成 Java 进程的堆内存转储(Heap Dump)或分析内存映射信息**jmap -heap {pid}**查看某进程堆状况
  • jmap可用于保存 Java 虚拟机的堆快照(Heap Dump),其主要功能及使用方法如下:

生成堆转储快照(Heap Dump)
将 JVM 当前时刻的堆内存状态保存为二进制文件(.hprof),供后续分析内存泄漏、对象分布等问题。
支持两种模式:
全部对象:jmap -dump:format=b,file=<filename.hprof>
仅存活对象:jmap -dump:live,format=b,file=<filename.hprof> (触发 Full GC 后仅保存存活对象,减少文件大小)。

  • jconsole 是 JDK 自带的图形化监控工具,基于 JMX(Java Management Extensions) 规范构建,用于实时监控和管理 Java 虚拟机(JVM)的运行状态。它提供了直观的可视化界面,帮助开发者诊断性能问题、分析资源消耗,并支持本地和远程 JVM 监控。
    jconsole界面
  • jvisualvm(Java VisualVM)是 JDK 自带的多功能图形化监控与分析工具,集成了性能监控、堆转储分析、线程诊断、MBean 管理等功能,适用于开发、测试及生产环境的 Java 应用分析。jvisualvm工具在jdk1.8后默认不添加,需要手动下载
    jvisualvm
    这里提供了了堆dump功能,堆导出,保存堆的快照,便于静态调试出出现问题的类
    使用该功能后,跳转到以下界面,右侧有查找最大对象功能,便于找出内存消耗较大的类
    在这里插入图片描述
  • javap 是 JDK 自带的反汇编工具,用于分析 .class 文件的字节码、类结构及成员信息。通过它,开发者可以深入了解 Java 编译后的代码逻辑、JVM 执行机制,以及进行调试和优化。
  1. 查看字节码指令
    使用 -c 选项反汇编方法代码,显示每条字节码指令(如 aload_0、invokevirtual),帮助理解编译器如何将源代码转换为 JVM 指令。
  2. 使用 -v(或 -verbose)输出详细信息,包括常量池(字符串、类引用等)、方法签名、行号表(调试用)等。
    示例:
    javap -c HelloWorld.class
参数效果
-c反汇编方法代码,显示字节码指令。
-p显示所有类成员(包括私有字段和方法)。
-v输出详细信息(常量池、行号表、局部变量表等)。
-s显示内部类型签名(JVM 规范格式)。
-l输出行号表和局部变量表(调试用)。
-classpath指定类路径(覆盖默认路径或 CLASSPATH 环境变量)。

方法区

方法区保存以下信息:

  1. 类的结构信息
  2. 运行时常量池
  3. JIT 编译后的代码
  4. 其他元数据
  • 方法区逻辑上是堆的一部分,不过这知识一个规范,jvm厂商不一定以这种方式实现。
  • 永久代:在JDK 8之前,方法区在HotSpot虚拟机中被称为永久代(PermGen),它位于堆内存中,与新生代、老年代共享堆空间。但永久代有固定大小,容易因类加载过多导致内存溢出(OutOfMemoryError: PermGen space)。
  • 元空间:从JDK 8开始,方法区被元空间(Metaspace)取代。元空间使用本地内存而非堆内存,极大缓解了类加载造成的内存溢出问题。元空间的大小可以通过JVM参数(如-XX:MetaspaceSize和-XX:MaxMetaspaceSize)进行调整。
    当方法区内存不足时,也会抛出OutOfMemoryError报错
    HotSpot虚拟机内存示意图:
    jvm内存结构
    如图hotspot虚拟机元空间使用的是操作系统内存,一般不会溢出

常量池

常量池包括 运行时常量池, Class 文件常量池(静态常量池)、字符串常量池 和 基本类型包装类常量池

使用javap -v查看反编译代码如下图,红色框内即为class文件常量池(静态常量池)
在这里插入图片描述
当运行程序时,*.class被加载,这些常量会被加载到内存,也就是运行时常量池,然而加载到内存并不是将这些类似#1 #2的标号加载进入内存,而是其真实的内存地址,虚拟机根据内存地址直接找到常量数据

  • StringTable 串池

是运行时常量池的子集,专门用于存储字符串字面量。JDK 1.6 及之前:字符串常量池位于方法区(永久代)。 JDK 1.7 及之后:移至堆内存,避免永久代内存溢出问题。
程序刚开始执行,串池为空,字符串加载到串池为懒加载,比如程序第一行是String s=“a”,此时jvm会去串池寻找是否存在该字符串(通过哈希快速查找),如果有,则直接把地址引用过去(这也就是为什么字符串不可更改的原因,字符串对象内容不可变,因此可以安全共享引用),如果没有则会通过Class 文件常量池间接获取字符串字面量,将其字符串常量加载到字符串常量池,并将其加载到局部变量表的slot栏里。

以下是某class文件反编译的字节码,其对应于源码String s=‘a’;
在这里插入图片描述ldc表示加载,这里加载#2,jvm会先去字符串常量池查找是否存在该字符串,若没有,将其加载到StringTable,一开始所有字符串并没有都加载到串池,而是用到时才加载到串池,这是一种懒加载
现在分析以下代码,来判断s3和s2是否一样:
在这里插入图片描述
其反编译字节码为:
在这里插入图片描述
可以看到在执行s3字符串赋值时,与前两种大不相同,其调用StringBuilder无参构造器创建了StingBuilder,并且调用append方法添加字符串a和b,最后通过StringBuilder的toString方法返回给了s3,toString方法代码如下:
在这里插入图片描述
在 Java 中,通过 new 创建的实例对象绝大多数情况下确实是在堆(Heap)内存中分配的显然是在堆里创建了一个新的字符串对象,并且将地址传给了s3.显然s3不等于s2

  • String a=“a”+“b” 编译优化
String a="a";
String b="b";
String c="a"+"b";
  • String c=“a”+“b"反编译对应以下代码,可以看出并没有借助StringBuilder,这是jvm优化后结果,变量c一定是一个定值"ab”,所以优化为String c=“ab”
  29: ldc           #4                  // String ab31: astore        5
  • 使用inter()方法主动将字符串放入串池
    例子:s.inter() ===》返回值 为 String 返回的一定是字符串常量池里的字符串

inter方法会先去字符串常量池里查看是否有该字符串,若有则直接返回字符串常量池的字符串;
若没有,则把当前的字符串放入常量池
这个方法可以在出现大量相同字符串前后缀使用,引用同一个字符串,可以降低大量内存
判断下面两题结果:
例1:

public class Test1 {public static void main(String[] args) {String a=new String("a")+new String("b");a.intern();String c="ab";System.out.println(a=="ab");}
}

执行结果:true
例2:

public class Test1 {public static void main(String[] args) {String a=new String("a")+new String("b");
//        a.intern();String c="ab";System.out.println(a=="ab");}
}

执行结果:false
其结果不一样的原因为:使用new String + new String创建的字符串在堆里,在常量池里没有ab时可能会复用堆中的 String 对象(即直接让常量池引用堆对象) 在不同虚拟机里可能表现不一样,在hotspot是这样的,同时这是以1.8后为准,在1.6及以前,intern是直接复制一份到常量池,而不是引用

  • 在jvm1.6前,StringTable串池在永久代,永久代在方法区里,然而永久代内存回收管理时间周期较长,当gc(内存回收)永久代时,StrinTable才会处理回收部分内存,在1.8后,StrinTable转到了堆里,同时,原来的永久代实现变为了元空间逻辑实现,元空间在系统内存。
    在这里插入图片描述

StringTable性能调优

  • -XX:StringTableSize 是 Java 虚拟机(JVM)中的一个非稳定参数(-XX 参数),用于设置字符串常量池(String Pool)的哈希表桶数量,旨在优化字符串的存储和查找效率,减少哈希冲突,从而提升性能。
    使用方法: -XX:StringTableSize={N}
    设置太少桶可能导致哈希碰撞严重,查找速度降低,太多则会占用更多内存,需要根据情况找到一种平衡;
  • 使用intern()进行字符串内存调优 十分重要的方法

假设有一个list不断的添加大量字符串对象,并且这些字符串有可能出现不少是重复的,我们可以选择使用list.add({字符串对象}.intern()),现在你应该明白为什么intern()方法会返回字符串常量池的字符串了,这样,在遇到相同字符串后,可以直接复用常量池的字符串,而无需再开辟一个新空间创建一个字符串对象。

直接内存 DirectMemory

java本身是不具备调用磁盘的权力,而是由调用本地方法调用空间,而这个过程需要由用户态转到内核态,文件先读取到系统缓冲区,系统缓冲区是java不可以运行的,需再从系统缓冲区里读取到java的缓冲区(在堆内存里),这个过程进行了数据复制,浪费了不少时间。
使用直接内存,可以由java直接读取访问,这段内存系统,java都可以直接访问。减少了复制的操作,速度更快了。

直接内存有以下特点

  • 常见于NIO操作时,用于数据缓冲区
  • 分配回收成本高,但读写性能强
  • 不受jvm内存关联

直接内存回收机制

  1. 直接内存本身不直接受JVM垃圾回收器管理,但它是通过DirectByteBuffer对象引用的。当DirectByteBuffer对象被GC判定为不可达(即没有强引用指向它)时,GC会将其标记为可回收对象。
    在DirectByteBuffer的构造过程中,会创建一个Cleaner对象(通过sun.misc.Cleaner或java.lang.ref.Cleaner),该Cleaner持有一个对直接内存的引用,并注册了一个回调函数(用于释放直接内存)。
  2. 当直接内存无引用且找不到GC Root时,其关联的ByteBuffer对象会被垃圾回收器回收,进而触发Cleaner机制通过Unsafe.freeMemory()释放内存,但这一过程存在延迟性,并非由Unsafe直接主动回收无引用的内存。
  3. Cleaner机制触发回收
    当DirectByteBuffer对象被GC回收时,Cleaner会被加入到一个待处理的队列中。
    后台的ReferenceHandler线程(优先级较高)会处理这个队列,调用Cleaner的回调函数(即sun.misc.Cleaner的clean()方法),最终通过Unsafe.freeMemory()释放直接内存。
    手动触发回收(不推荐)
  4. 可以通过调用System.gc()建议JVM执行垃圾回收,但无法保证立即回收直接内存。
    也可以通过反射调用Cleaner的clean()方法强制释放,但这种方式破坏了封装性,可能导致不可预测的行为。
    使用ByteBuffer bytebuffer=ByteBuffer.allocateDirect(int capacity)可分配直接内存
http://www.dtcms.com/a/389802.html

相关文章:

  • 容器查看日志工具-stern
  • 衍射光学元件DOE:台阶高度与位置误差的测量
  • Java中对象/嵌套对象属性复制工具类使用示例:Hutools工具类BeanUtils使用示例
  • rust编写web服务02-路由与请求处理
  • Spring Cloud - 微服务限流的方式
  • 【智能系统项目开发与学习记录】ROS2基础(1)
  • 人工智能面试题:什么是CRF条件随机场
  • [x-cmd] 命令式交互、CLI/TUI 设计与 LLM
  • 基于AMBA总线协议的Verilog语言模型实现
  • 【Agent项目复现】OpenManus复现
  • 高校AI虚拟仿真实训平台软件解决方案
  • Vue3 + Ant Design Vue 实现统一禁用样式管理方案,禁用状态下已有值颜色区分(CSS 变量方案)
  • Ubuntu 24.04部署MongoDB
  • 8.1-spring 事务-声明式事务(使用)
  • Vue3》》组件继承 extends
  • 无人系统在边境管控的应用探讨
  • 一个典型的mysql数据库连接池初始化函数
  • novel英文单词学习
  • 数据结构:树及二叉树--堆(下)
  • TDengine 聚合函数 STDDEV 用户手册
  • ARM--启动代码
  • openharmony1.1.3 通过i2c进行温湿度采集
  • 虚拟仿真技术赋能国土资源监测教育,破解生态与安全人才培养困局
  • Vim 详细使用方法与运维工作常用操作
  • python基础数据分析与可视化
  • DeepSort学习与实践-原理学习
  • 贪心算法应用:多重背包启发式问题详解
  • 使用C#开发的控笔视频生成小程序
  • [重学Rust]之ureq
  • 水下机器人专用绝缘监测装置