常用的 JVM 调优的命令都有哪些?
jps:列出本机所有 Java 进程的进程号。
常用参数如下:
-m
输出main
方法的参数-l
输出完全的包名和应用主类名-v
输出JVM
参数
jps -lvm
//output
//4124 com.zzx.Application -javaagent:E:\IDEA2019\lib\idea_rt.jar=10291:E:\IDEA2019\bin -Dfile.encoding=UTF-8
jstack:查看某个 Java 进程内的线程堆栈信息。使用参数-l
可以打印额外的锁信息,发生死锁时可以使用jstack -l pid
观察锁持有情况。
jstack -l 4124 | more
输出结果如下:
"http-nio-8001-exec-10" #40 daemon prio=5 os_prio=0 tid=0x000000002542f000 nid=0x4028 waiting on condition [0x000000002cc9e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000077420d7e8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
WAITING (parking)
指线程处于挂起中,在等待某个条件发生,来把自己唤醒。
jstat:用于查看虚拟机各种运行状态信息(类装载、内存、垃圾收集等运行数据)。使用参数-gcuitl
可以查看垃圾回收的统计信息。
jstat -gcutil 4124
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 67.21 19.20 96.36 94.96 10 0.084 3 0.191 0.275
参数说明:
- S0:
Survivor0
区当前使用比例 - S1:
Survivor1
区当前使用比例 - E:
Eden
区使用比例 - O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
jmap:查看堆内存快照。通过jmap
命令可以获得运行中的堆内存的快照,从而可以对堆内存进行离线分析。
查询进程4124的堆内存快照,输出结果如下:
>jmap -heap 4124
Attaching to process ID 4124, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11
using thread-local object allocation.
Parallel GC with 6 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 4238344192 (4042.0MB)
NewSize = 88604672 (84.5MB)
MaxNewSize = 1412431872 (1347.0MB)
OldSize = 177733632 (169.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 327155712 (312.0MB)
used = 223702392 (213.33922576904297MB)
free = 103453320 (98.66077423095703MB)
68.37795697725736% used
From Space:
capacity = 21495808 (20.5MB)
used = 0 (0.0MB)
free = 21495808 (20.5MB)
0.0% used
To Space:
capacity = 23068672 (22.0MB)
used = 0 (0.0MB)
free = 23068672 (22.0MB)
0.0% used
PS Old Generation
capacity = 217579520 (207.5MB)
used = 41781472 (39.845916748046875MB)
free = 175798048 (167.65408325195312MB)
19.20285144484187% used
27776 interned Strings occupying 3262336 bytes.
对象头了解吗?
Java 内存中的对象由以下三部分组成:对象头、实例数据和对齐填充字节。
而对象头由以下三部分组成:mark word、指向类信息的指针和数组长度(数组才有)。
mark word
包含:对象的哈希码、分代年龄和锁标志位。
对象的实例数据就是 Java 对象的属性和值。
对齐填充字节:因为JVM要求对象占的内存大小是 8bit 的倍数,因此后面有几个字节用于把对象的大小补齐至 8bit 的倍数。
内存对齐的主要作用是:
1.平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
3.main方法执行过程
示例代码:
public class Application {
public static void main(String[] args) {
Person p = new Person("大彬");
p.getName();
}
}
class Person {
public String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
-
执行
main
方法的过程如下:- 编译
Application.java
后得到Application.class
后,执行这个class
文件,系统会启动一个JVM
进程,从类路径中找到一个名为Application.class
的二进制文件,将Application
类信息加载到运行时数据区的方法区内,这个过程叫做类的加载。 - JVM 找到
Application
的主程序入口,执行main
方法。 main
方法的第一条语句为Person p = new Person("大彬")
,就是让 JVM 创建一个Person
对象,但是这个时候方法区中是没有Person
类的信息的,所以 JVM 马上加载Person
类,把Person
类的信息放到方法区中。- 加载完
Person
类后,JVM 在堆中分配内存给Person
对象,然后调用构造函数初始化Person
对象,这个Person
对象持有指向方法区中的 Person 类的类型信息的引用。 - 执行
p.getName()
时,JVM 根据 p 的引用找到 p 所指向的对象,然后根据此对象持有的引用定位到方法区中Person
类的类型信息的方法表,获得getName()
的字节码地址。 - 执行
getName()
方法。
- 编译
4.对象创建过程
- 类加载检查:当虚拟机遇到一条
new
指令时,首先检查是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那先执行类加载。 - 分配内存:在类加载检查通过后,接下来虚拟机将为对象实例分配内存。
- 初始化。分配到的内存空间都初始化为零值,通过这个操作保证了对象的字段可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
- 设置对象头。
Hotspot
虚拟机的对象头包括:存储对象自身的运行时数据(哈希码、分代年龄、锁标志等等)、类型指针和数据长度(数组对象才有),类型指针就是对象指向它的类信息的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 - 按照
Java
代码进行初始化。
如何排查 OOM 的问题?
排查 OOM 的方法:
- 增加JVM参数
-XX:+HeapDumpOnOutOfMemoryError
和-XX:HeapDumpPath=/tmp/heapdump.hprof
,当 OOM 发生时自动 dump 堆内存信息到指定目录; - jstat 查看监控 JVM 的内存和 GC 情况,评估问题大概出在什么区域;
- 使用 MAT 工具载入 dump 文件,分析大对象的占用情况 。
GC是什么?为什么要GC?
GC 是垃圾收集的意思(Gabage Collection)。内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
Java学习视频
Java基础:
Java300集,Java必备优质视频_手把手图解学习Java,让学习成为一种享受