1. 获取 JVM 内存信息的方法
1.1 综合性图形化工具
如:JConsole、VisualVM(GitHub)、 VisualVM(JDK tool)、Java Mission Control(JMC)等
其中,JFR(Java Flight Recorder)+ JMC 算是针对生产环境中查看 JVM 信息的典型方法之一。
具体操作就是:
-
先让Java进程开启JFR特性运行一段时间,从而将收集到的JVM信息导出到指定的 jfr 文件中
在JVM启动参数中添加“-XX:+UnlockCommercialFeatures -XX:+FlightRecorder”即可开启JFR特性。
可以在启动参数中指定数据收集时长与导出文件
-XX:StartFlightRecording=duration=60s,filename=my-app.jfr
也可以在Java进程运行一段时间后,再通过 jcmd 开启数据收集并指定导出文件
jcmd $PID JFR.start duration=60s filename=my-app.jfr
-
再通过 JMC 导入上述 jfr 文件,在JMC中查看分析相关数据
包括CPU、内存、线程、IO、各种事件及其它操作系统信息等
1.2 命令行工具
如:jstat、jmap、jstack 等
1.2.1 jstat
如:通过命令 “jstat -gc 49379 3000 5” 查看进程号为49379的Java进程GC相关的堆统计信息,每个3000毫秒统计一次,共统计5次
[root@localhost bin]# jstat -gc 49379 3000 5
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
17472.0 17472.0 0.0 1576.0 139968.0 10169.5 349568.0 40525.3 72408.0 70025.4 8704.0 8172.9 19 2.869 5 0.468 3.337
17472.0 17472.0 0.0 1576.0 139968.0 10417.3 349568.0 40525.3 72408.0 70025.4 8704.0 8172.9 19 2.869 5 0.468 3.337
17472.0 17472.0 0.0 1576.0 139968.0 10956.6 349568.0 40525.3 72408.0 70025.4 8704.0 8172.9 19 2.869 5 0.468 3.337
17472.0 17472.0 0.0 1576.0 139968.0 11661.5 349568.0 40525.3 72408.0 70025.4 8704.0 8172.9 19 2.869 5 0.468 3.337
17472.0 17472.0 0.0 1576.0 139968.0 12173.4 349568.0 40525.3 72408.0 70025.4 8704.0 8172.9 19 2.869 5 0.468 3.337
1.2.2 jmap
如:
- 通过命令 “jmap -heap 49379” 查看进程号为49379的Java进程堆信息
[root@localhost bin]# jmap -heap 49379
Attaching to process ID 49379, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.152-b16
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 1073741824 (1024.0MB)
NewSize = 178913280 (170.625MB)
MaxNewSize = 357892096 (341.3125MB)
OldSize = 357957632 (341.375MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB) # 元数据区
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 161218560 (153.75MB)
used = 130447904 (124.40481567382812MB)
free = 30770656 (29.345184326171875MB)
80.91370125127033% used
Eden Space:
capacity = 143327232 (136.6875MB)
used = 123462536 (117.74304962158203MB)
free = 19864696 (18.94445037841797MB)
86.14031979631058% used
From Space:
capacity = 17891328 (17.0625MB)
used = 6985368 (6.661766052246094MB)
free = 10905960 (10.400733947753906MB)
39.04331752232143% used
To Space:
capacity = 17891328 (17.0625MB)
used = 0 (0.0MB)
free = 17891328 (17.0625MB)
0.0% used
tenured generation: # 老年代
capacity = 357957632 (341.375MB)
used = 57555232 (54.888946533203125MB)
free = 300402400 (286.4860534667969MB)
16.07878331254577% used
32415 interned Strings occupying 3713496 bytes.
- 通过命令 “jmap -dump:format=b,file=dump.bin 49379” 将进程号为49379的Java进程的堆信息导出到文件 dump.bin 中;
- 再将导出的文件导入到VirtualVM之类的图形化工具中查看详细信息;
- 或通过 jhat 加载导出的文件进行查看(不过远没有图形化工具友好实用)
[root@localhost bin]# jmap -dump:format=b,file=dump.bin 49379
Dumping heap to /opt/dump.bin ...
Heap dump file created
1.2.3 jstack
如:通过命令 “jstack -l 49379” 查看进程号为49379的Java进程的栈信息,包括与锁相关的额外信息
[root@localhost bin]# jstack -l 49379
...
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f0a0c07a800 nid=0xc0e7 in Object.wait() [0x00007f0a11632000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000d578d6d0> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers:
- None
"VM Thread" os_prio=0 tid=0x00007f0a0c073000 nid=0xc0e6 runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f0a0c0be800 nid=0xc0ed waiting on condition
JNI global references: 557
1.3 其它工具
注:JVM开启对NMT的支持会导致性能有所下降。
Native Memory Tracking 写道
Enable NMT using the following command line. Note that enabling this will cause 5-10% performance overhead.
如果配合 Javaassist 可以更方便地从字节码层面操控Java程序,获取更多样化的信息
2. OOM诊断
除了 程序计数器(PC),其它 JVM内存区 都有可能发生 OutOfMemeryError
2.1 OOM种类含义
出现OOM意味着:
- GC已经无法提供足够的内存存放新对象,堆的容量也无法扩展
- 或本地内存无法提供足够的空间载入Java类
- 极少数情况下,可能是因为GC持续了很长时间但几乎没有内存被释放
诊断OOM的第一步是定位其来源。然后再根据具体的错误信息结合程序代码得出真正的解决方案。
因为只知哪片内存区出现OOM而直接增大该区域空间往往抓不住问题根源,得不出真正的解决方案。
2.1.1 Java heap space
java.lang.OutOfMemoryError:Java heap space
具体可能原因:
- 内存泄漏
- 堆大小设置不合理
- JVM处理引用不及时,导致内存无法释放
2.1.2 GC Overhead limit exceeded
java.lang.OutOfMemoryError: GC Overhead limit exceeded
Oracle的官方解释是当JVM花了绝大部分时间去GC但回收的内存太少时会抛出该类型的OOM;并给出了一个宽泛的解决方法——增加堆内存。
实际使用场景中,还是需要根据详细的错误信息结合程序的代码进一步分析,再得出真正的解决方案。因为可能是程序逻辑的问题导致创建太多对象(如,在死循环内创建对象)。
《Understand the OutOfMemoryError Exception》 写道
After a garbage collection, if the Java process is spending more than approximately 98% of its time doing garbage collection and if it is recovering less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections, then a java.lang.OutOfMemoryError is thrown.
2.1.3 Requested array size exceeds VM limit
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
程序试图创建一个数组时,该数组的大小超过了剩余可用(连续空间)的堆大小。
出现这种情况可能是堆设置得太小了。也有可能是程序在选择数组容量大小的逻辑有问题,特别是当该容量是通过某种算法动态决定的情况。
2.1.4 Metaspace
java.lang.OutOfMemoryError: Metaspace
可通过JVM参数 MaxMetaSpaceSize 设置元数据区的最大容量
永久代 被 元数据区 替代后,“方法区”出现OOM的情况会少一些。因为元数据区空间默认会自增
2.1.5 request size bytes for reason. Out of swap space
java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space
Oracle的官方解释是,本地内存(native heap)不够用导致的。
为了找到深层原因,可能得分析相关错误日志;同时可以结合其它操作系统运维工具进行排查。
错误日志的具体路径可以通过JVM启动参数配置。如:-XX:ErrorFile=/var/log/java/java_error.log
2.1.6 Compressed class space
java.lang.OutOfMemoryError: Compressed class space
可通过JVM启动参数配置增大相应的内存区域。如:-XX:CompressedClassSpaceSize=2g
2.1.7 reason stack_trace_with_native_method
java.lang.OutOfMemoryError: reason stack_trace_with_native_method
一般出现该错误时,线程正在执行一个本地方法。也就是说执行本地方法时内存不足。
可以通过操作系统运维工具进一步排查。
2.1.8 其它
Java虚拟机栈 和 本地方法栈
- 过深的递归调用导致栈空间不足时,一般抛出 StackOverFlowError,栈溢出
- 当 JVM试图扩展栈空间失败时,抛出 OOM
老版本的永久代内存区
- 错误信息:java.lang.OutOfMemoryError:PermGen space
- 永久代大小有限,且GC不积极,在某些场景下容易抛 OOM。如:
- 运行时大量生成动态类型
- Intern 字符串缓存过多
3. 内存泄漏诊断
诊断内存泄漏是非常繁琐的事情,需要长时间尝试Java程序的运行场景并监视内存对象状态。
可以利用前述的各种工具协助排查。
如,查看待回收的对象数:
- 可以通过 JConsole等图形化工具
- 也可以通过 “jmap -finalizerinfo [pid]”
如果条件允许,可供选择的方式/工具非常多,甚至可以自己开发小工具。
java.lang.management、java.lang.instrument、java.lang.Thread、JVM Tool Interface、JPDA(Java Platform Debugger Architecture)等都可以尝试。
思考
当试图分配一个 100MB 的数组时发生 OOM,但GC日志显示可用堆空间大于 100MB,可能是什么原因?
总体思路
- 分配一个数组需要一块连续的内存空间
- GC日志显示的可用空间大小是总大小,而这些可用空间可能是由不连续的多个空间合成的;如果这多个空间中没有一个是大于100MB的,就会抛出OOM
具体可能情况
- 如果如果新生代的GC机制是 serial,会默认使用 copying 算法;当遇到大对象时会直接将大对象放到老年代中
- 此时,如果老年代的GC机制是 serial old,则会使用 mark compact 方式,试图在老年代中整理出一块足够大的连续空间。如果获取空间失败,则OOM
- 如果,此时老年代的GC机制是 CMS (Concurrent Mark Sweep),老年代的内存区只会被标记清理,不会被“压缩”整理,也就会出现碎片化。这样即使老年代的可用空间就已大于100MB,也可能出现OOM
- 如果采用了G1收集垃圾,那就可能是没有多个连续的 region 内存之和超过100MB
- 大小: 81.3 KB
分享到:
相关推荐
4. **内存溢出与内存泄漏**:内存溢出是JVM无法分配新的内存,内存泄漏则指程序未释放不再使用的内存。识别和解决这些问题对优化性能至关重要。 5. **JVM参数调优**:如-Xms和-Xmx设置堆大小,-XX:NewRatio调整...
- JVM是Java平台的核心组成部分,它是一个运行Java字节码的虚拟机,负责执行Java程序。 - JVM实现了Java的跨平台特性,即“一次编写,到处运行”。 2. **类加载机制**: - 类的生命周期包括加载、验证、准备、...
##### 2.2 进阶JVM技术 - **性能监控与故障排查**:使用`VisualVM`、`JConsole`等工具进行监控。 - **内存泄漏诊断**:定位内存泄漏的原因,使用`MAT`等工具进行分析。 - **JVM参数调优**:掌握关键参数如`-Xms`、`...
jvisualvm作为一款强大的JVM监控工具,是Java开发者日常性能调优的得力助手。通过深入理解并熟练运用其各项功能,我们可以更有效地管理和优化Java应用,提升系统性能,减少故障发生。在实际工作中,结合日志分析、...
6. **性能监控与诊断工具**:如VisualVM、JConsole、JProfiler等工具的使用,帮助开发者实时查看和分析JVM的状态。 7. **并发与多线程**:JVM如何处理并发,包括线程同步、锁机制、线程池的配置与优化。 8. **JIT...
《深入JVM内核—原理、诊断与优化》是一份全面涵盖Java虚拟机核心知识的教程,共计11个章节,旨在帮助读者深入理解JVM的工作原理,掌握故障诊断技巧,并能进行有效的性能优化。这份资料是每一个Java开发者进阶的必备...
6. **性能监控和诊断工具**:如JConsole、VisualVM、JProfiler等,它们可以帮助开发者监控JVM状态,定位性能瓶颈。 7. **JVM参数**:学习如何设置JVM启动参数,如-Xms, -Xmx控制堆大小,-XX:+UseConcMarkSweepGC...
6. **监控与诊断工具**:JDK自带的JConsole、VisualVM、JProfiler等工具可以帮助我们实时监控JVM状态,分析CPU、内存、线程等问题,找出性能瓶颈。 7. **JVM内存泄漏检测**:学习如何识别和处理内存泄漏问题,了解...
在IT行业中,JVM(Java ...以上知识点涵盖了JVM监控的基础和进阶知识,通过熟练掌握这些工具和概念,可以有效提升Java应用的稳定性和性能。在实际操作中,需要根据具体应用需求选择合适的工具和方法进行监控和分析。
5. **第26讲 - JVM内存结构-监控和诊断**:JVM内存结构包括堆、栈、方法区等,这部分可能讲解了如何理解这些区域的工作方式,以及如何使用JVisualVM等工具进行内存监控和问题诊断。 6. **第21讲 - java线程池**:...
4. **虚拟机接口模块**:提供与Java语言交互的接口,包括JNI(Java Native Interface)和JVM TI(Java Virtual Machine Tool Interface),它们允许C/C++代码与Java代码交互,以及调试和监控工具接入。 5. **服务...
通过监控CPU和内存使用情况,可以发现潜在的性能瓶颈。JVisualVM和VisualVM等工具提供了丰富的性能分析功能,帮助我们优化代码。 总的来说,这门"JAVA高端进阶开发课程"将全面覆盖Java应用程序调试的各个方面,不仅...
- **性能监控与诊断**:使用jconsole、jvisualvm等工具进行JVM监控。 这些面试题涉及到的Java核心技术是开发者必须掌握的基础,熟练运用这些知识可以提升代码质量,优化程序性能,解决实际问题。对于面试者来说,...
Java筑基面试专题系列是针对Java开发者进阶的深度学习资料,主要涵盖了并发编程、Netty框架以及JVM这三个核心领域。在这个专题中,我们将会深入探讨这些关键知识点,以便在面试中展现出扎实的技术功底。 一、并发...
Java虚拟机(JVM)是Java编程语言的核心组成部分,它为Java程序提供了运行环境。深入理解JVM对于优化代码性能、解决内存问题以及提升开发效率至关重要。本教程将全面讲解JVM的工作原理、内存管理、类加载机制以及...
2. **JVM性能监控与诊断** - **JDK工具**:介绍JConsole、VisualVM、JProfiler等工具的使用,用于实时监控JVM状态,诊断性能瓶颈。 - **JMX(Java Management Extensions)**:解释了如何利用JMX进行远程管理和...
2. **Java进阶**:重点关注并发编程和Java虚拟机(JVM)的知识,如线程管理、内存模型、垃圾回收等,这是Java高级面试的重头戏。 3. **Java应用开发扩展**:涵盖数据库编程、主流开源框架如Spring、MyBatis等的使用...
这本书涵盖了从JVM的架构到内存管理,从类加载机制到垃圾收集,从编译优化到运行时性能监控等多个核心主题,是Java程序员进阶必备的参考资料。 首先,我们来深入了解Java虚拟机的基本概念。JVM是Java平台的核心组成...
6. **JVM性能监控与故障排查**:提供了使用JDK自带的JConsole、VisualVM等工具进行性能分析和问题诊断的方法。 7. **JVM调优**:讨论了如何调整JVM参数以提升系统性能,包括堆大小设置、GC策略选择、线程池配置等。...