Jvm内存以及垃圾回收相关知识

堆内存相关概念

  1. 已提交的堆内存(Committed Heap Memory)
    • 这是JVM已经向操作系统申请并预留的堆内存大小。这部分内存是JVM可以使用的,但可能并未完全被使用(即已提交的内存可能大于当前已使用的内存)。
    • 提交的内存是连续的虚拟地址空间,并且操作系统已经为这部分地址空间预留了物理内存(可能通过分页机制,不一定全部在物理内存中,但已经确保在需要时能够分配物理内存)。
  2. 最大的堆内存(Maximum Heap Memory)
    • 这是JVM堆内存可以扩展到的最大大小。这个值通常由JVM启动参数 -Xmx 指定。
    • 最大堆内存是JVM堆内存的上限,已提交的堆内存不会超过这个值。
  3. 已使用的堆内存
    • JVM实际已使用的内存
  4. 关系
  • JVM启动时,初始堆内存大小由 -Xms 指定,此时JVM会提交初始堆内存(即已提交的堆内存至少为初始堆大小)。
  • 随着应用程序运行,当已使用的堆内存接近已提交的堆内存时,JVM可能会尝试增加已提交的堆内存(即向操作系统申请更多内存),直到达到最大堆内存。
  • 如果已提交的堆内存小于最大堆内存,JVM可以根据需要继续申请。如果已提交的堆内存已经达到最大堆内存,那么即使内存不足,也无法再申请,此时如果堆内存已满,就会触发垃圾回收,如果回收后仍然不足,则会抛出OutOfMemoryError。
  1. 容易误解的点
  • JVM已提交的内存一开始其实都是虚拟内存,并不会实际就占用操作系统内存,只是这块内存,操作系统会预留给JVM。因此通过top命令或者任务管理器看java进程占用的内存时,其实很少。但是一旦真实分配过内存后,就会开始占用操作系统的内存了,即便后面发生了垃圾回收,jvm内部的堆内存降低,操作系统看java进程占用的内存也不会变少,因为jvm通常是不会把回收的内存归还给操作系统的。

常用启动参数

  • 最大堆:-Xmx4g -Xmx128m

  • 初始堆:-Xms4g -Xms128m

  • k8s容器环境配置初始堆以及最大堆,不要固定写死,不然无法根据k8s来弹性扩容,使用如下参数

    -XX:InitialRAMPercentage=70 -XX:MaxRAMPercentage=70,即取容器内存的70%

    注: JDK8u191+版本才比较成熟

  • -XX:+HeapDumpOnOutOfMemoryError:发生内存溢出时自动导出内存映射文件

  • -XX:HeapDumpPath:指定内存溢出时导出内存映射文件的位置,一般与-XX:+HeapDumpOnOutOfMemoryError搭配使用

    使用形式有两种

    只指定目录:XX:HeapDumpPath=D:/dump,则文件名会自动生成,形如java_pid1234.hprof,其中1234为java进程的pid

    指定全路径:XX:HeapDumpPath=D:/dump/dump.hprof

常用工具

jinfo

查看所有生效的jvm参数

jinfo -flags 1234

查看具体某一个参数

jinfo -flag MaxHeapSize 1234

jmap

dump所有对象

jmap -dump:format=b,file=D:\dump\java_pid1234.hprof 1234 其中1234为pid

不会触发full gc,但是也会stw。没有被回收的垃圾对象也会被包含在其中。

只dump存活对象

jmap -dump:live,format=b,file=D:\dump\java_pid1234.hprof 1234

这样会先full gc,再dump。使得文件更小,垃圾对象被清理,更容器分析真正泄漏对象。但是stw时间会更久,生产环境高峰期慎用。

查看堆信息

jmap -heap pid

可以看到详细的堆信息,最大堆,初始堆,老年代(old)、年轻代(eden、survivor)容量信息

查看对象数量排行

jmap -histo 12345

jmap -histo:live 12345(只统计存活对象,会触发full gc)

jstack

jstack 12345

jcmd

jcmd整合了jinfo、jmap、jstack、jps等命令的功能,jdk高版本主推工具。底层attach机制更现代化,更先进,推荐使用。

列出所有可用的子命令

jcmd pid help,pid为具体的java进程id

若pid指定为0,相当于先找出所有的java进程id,挨个调用jcmd pid help

子命令帮助

jcmd pid help GC.heap_dump,pid为具体的java进程id

列出java进程(jps)

jcmd -l

dump内存文件(jmap -dump)

jcmd 12345 GC.heap_dump heap.hprof,dump存活对象,会触发full gc

jcmd 12345 GC.heap_dump -all heap.hprof,dump所有对象,包括不可达对象

查看堆信息(jmap -heap)

jcmd 12345 GC.heap_info

查看对象排行(jmap -histo)

jcmd 12345 GC.class_histogram | head -n 10 取前10名,只统计存活对象,会触发full gc

jcmd 12345 GC.class_histogram -all 统计所有对象,包括不可达对象。

输出格式

num #instances #bytes class name

1: 53616 6220928 [C
2: 20277 1784376 java.lang.reflect.Method
3: 53463 1283112 java.lang.String
4: 9860 1091680 java.lang.Class
5: 28646 916672 java.util.concurrent.ConcurrentHashMap$Node
6: 3542 611448 [B

依次为序号、实例数量、实例总字节数、类名

按照(bytes)实例总字节数降序排列。

查看线程信息(jstack)

jcmd 12345 Thread.print

查看java进程启动的命令行参数

jcmd 12345 VM.command_line

垃圾回收相关概念

并行:并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在协同工作,通常默认此时用户线程是处于等待状态。

并发:并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾收集器线程与用户线程都在运行。由于用户线程并未被冻结,所以程序仍然能响应服务请求,但由于垃圾收集器线程占用了一部分系统资源,此时应用程序的处理的吞吐量将受到一定影响。

吞吐量:运行用户代码时间/(运行用户代码时间+运行垃圾收集时间),该指标越高越好。

停顿时间:垃圾收集时暂停用户线程的时间,该指标越短越好。

停顿时间越短就越适合需要与用户交互或需要保证服务响应质量的程序,良好的响应速度能提升用户体验;而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的分析任务。

吞吐量和停顿时间是相互排斥的,一般垃圾回收期都是优先满足一个指标后,尽量满足另外一个指标。想要高吞吐量,意味着垃圾回收频率不能太频繁,当真正发生垃圾回收时,垃圾积累的多,那么清理就越慢,停顿时间自然就长。反之亦然。

常用垃圾收集器

Parallel Scavenge + Parallel Old

并行收集器,jdk8默认的垃圾收集器,Parallel Scavenge用于年轻代,使用复制算法,Parallel Old用于老年代,使用标记整理算法。

是一个吞吐量优先的垃圾收集器,设计的目标是达到一个可控制的吞吐量。

常用参数:

-XX:+UseParallelGC,使用并行收集器,jdk8默认开启。

-XX:GCTimeRatio,用于设置吞吐量,值应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率。譬如把此参数设置为19,那允许的最大垃圾收集时间就占总时间的5%,即1/(1+19)),默认值为99,即允许最大1%(即1/(1+99))的垃圾收集时间。

-XX:MaxGCPauseMillis,用于设置停顿时间,允许的值是一个大于0的毫秒数,没有固定的默认值,若设置,收集器将尽力保证内存回收花费的时间不超过用户设定值。但是设置的过少,则会影响到吞吐量。

-XX:+UseAdaptiveSizePolicy,开启自适应调节策略,默认开启,这个参数被激活之后,就不需要人工指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。

posted on 2026-06-10 22:20  wastonl  阅读(0)  评论(0)    收藏  举报