GC算法
引用计数法 循环引用
可达性分析法 GC Roots对象
栈中对象
方法区中常量/静态属性对象
本地方法栈中对象
可回收区域
新生代 老年代 永久区(废弃的常量/类)
回收算法 标记不被回收的对象
标记清除算法 碎片
复制算法 浪费一半空间
标记整理算法
分代回收算法 新生代(分代/标记清理) 老年代(标记整理)
收集器
Serial 单线程 复制算法 stw 没有线程切换 效率高
ParNew Serial多线程版本 可通过-XX:ParallelGCThreads限制线程个数
Parallel Scavenge 多线程 新生代 复制算法 控制吞吐量 服务运行了100分钟,GC用了1分钟, 吞吐量是99%
-XX:MaxGCPauseMillis 最大垃圾收集停顿时间 毫秒 不能过小, 太小了会增加频次
-XX:GCTimeRatio设置吞吐量大小
Serial Old 单线程 标记整理
Parallel Old Parallel Scavenge老年代版本 标记整理 可与Parallel Scavenge结合, 提升性能, 否则只能使用Serial Old回收老年代
CMS 最短停顿时间 产生浮动垃圾 标记清除 碎片 占用CPU 使用最多 CMS+ParNew
初始标记 并发标记 重新标记 并发清除
初始/并发标记都会stw
初始标记 找到GC Root直接关联对象, 很快
并发标记 根据初始标记的对象, 进行可达性分析, 标记出其他不被回收的对象
重新标记 标记出并发标记过程中产生的垃圾
-XX:+UseCMSCompactAtFullCollection参数默认开启, 用于当进行full GC时, 进行碎片整理
浮动垃圾 并发清除过程中, 用户线程执行会产生新的垃圾, 当次GC清理不了, 所以不能等空间完全使用时再清理, 否则并发清理时用户线程不能执行
G1 JDK9默认GC器
并发缩短stw时间 分代回收 整体看基于标记清理 局部看基于复制算法 无碎片
可预测停顿时间
将整个堆划分为多个大小相等的Region, 虽然还保留有新生代和老年代的概念, 但新生代和老年代不是物理隔离的了, 它们都是一部分可不连续Region的集合
OOM
年老代空间被占满 最常见 内存泄露 并发量高 大对象
永久代被占满 一般不会发生 但是大量使用反射会出现OOM
StackOverflowError
递归异常没有结束, 超出栈允许的最大深度, 栈空间有限. 栈帧越大, 栈深度越小, 栈帧越小, 栈深度越大
JVM参数
-Xms 初始堆大小 –
-Xmx 最大堆大小 –
-Xmn 年轻代大小(1.4or lator)整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8 –
-XX:newSize 表示新生代初始内存的大小,应该小于 -Xms的值 –
-XX:NewRatio 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 –
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4) –
-XX:PermSize 设置持久代(perm gen)初始值 –
-XX:MaxPermSize 设置持久代最大值 –
-Xss 每个线程的堆栈大小 –
-XX:ThreadStackSize – –
-XX:SurvivorRatio Eden区与Survivor区的大小比值, 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10 –
-XX:+DisableExplicitGC 关闭System.gc() –
-XX:MaxTenuringThreshold 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率 –
-Xnoclassgc 禁用类垃圾回收 –
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配,单位字节 新生代采用Parallel Scavenge GC时无效另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象. –
-XX:+CollectGen0First FullGC时是否先YGC
-XX:+UseParallelGC 选择垃圾收集器为并行收集器。此配置仅对年轻代有效。可以同时并行多个垃圾收集线程,但此时用户线程必须停止。 –
-XX:+UseParNewGC 设置年轻代收集器ParNew –
-XX:ParallelGCThreads Parallel并行收集器的线程数 –
-XX:+UseParallelOldGC 设置老年代的并行收集器是ParallelOld –
-XX:+UseG1GC 使用G1收集器 –
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间) –
-XX:+UseAdaptiveSizePolicy 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开. –
-XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的,百分比公式为1/(1+n) –
-XX:+ScavengeBeforeFullGC Full GC前调用YGC true
-XX:+UseConcMarkSweepGC 使用CMS内存收集
-XX:+PrintGC 输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]Full GC 121376K->10414K(130112K), 0.0650971 secs]
调优需要根据自己的业务场景进行调整, 没有万能的参数, 示例 -Xmx256m -Xms256m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs类加载过程
装载 连接(验证/准备/解析) 初始化
装载 读取二进制文件 创建一个Class对象
验证 校验二进制文件正确性
准备 为静态变量分配内存, 并设置默认值
解析 把符号引用转成直接引用
初始化 为静态变量赋值
类被初始化时机
- new类的一个对象
- 访问或赋值静态变量
- 调用静态方法
- Class.forName(“com.test.Test”)
- 初始化子类时, 父类会先初始化
- JVM启动参数标记启动类
Bootstrap->Extension->App->Customer
Bootstrap加载rt下的包
Extension加载jre/lib/*.jar
App加载classpath中指定的jar包及目录中class
Customer自定义加载器一般不使用, tomcat会用到
jps类似于linux下的ps命令, 查看当前运行的java进程
C:\>jps
11072 BrokerStartup
34240 ProviderApplication
22468 NamesrvStartup
29956 Launcher
7428 EurekaApplication
14152 CoreServiceApplication
5256 Program
30188 Launcher
22832
28340 Launcher
15352 Main
33560 Jps
27612 Launcher
37692 Launcher
jstat查看指定进程堆统计信息
gc 展示空间使用大小
C:\>jstat -gc 34240
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
23552.0 21504.0 0.0 21056.0 442880.0 245454.9 219136.0 60718.7 59160.0 56039.4 8064.0 7429.9 17 0.779 3 1.001 1.779
-gcutil 展示空间百分比
C:\>jstat -gcutil 34240
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 97.92 56.06 27.71 94.73 92.14 17 0.779 3 1.001 1.779
s0/s1 幸存者区
E 新生代
O 老年代
M 元空间
YGC YoungGC次数
YGCT YoungGC时间
FGC FullGC次数
FGCT FullG时间
GCT GC总占用时间
jmap dump堆信息
C:\>jmap -F -dump:format=b,file=test.hprof 30188
Attaching to process ID 30188, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.231-b11
Dumping heap to test.hprof ...
Heap dump file created
使用jvisualvm查看文件
jvisualvm是java提供的用于性能分析的工具
按照对象类型分类, 可查看示例大小, 个数, 双击可查看实例详情
jstack打印栈信息
创建死锁场景
public class DeadLockTest {
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (obj1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
}
}
}).start();
new Thread(()->{
synchronized (obj2) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
}
}
}).start();
}
}
查看栈信息, 可自动检测到死锁
C:\>jstack 36940
2020-12-02 20:10:08
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.231-b11 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x000000001f2ac000 nid=0x29e8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001f2a7000 nid=0x7d94 waiting for monitor entry [0x000000001fb2e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.test.demo.DeadLockTest.lambda$main$1(DeadLockTest.java:32)
- waiting to lock <0x000000076bf9e208> (a java.lang.Object)
- locked <0x000000076bf9e218> (a java.lang.Object)
at com.test.demo.DeadLockTest$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001f2a4800 nid=0x4be0 waiting for monitor entry [0x000000001fa2f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.test.demo.DeadLockTest.lambda$main$0(DeadLockTest.java:19)
- waiting to lock <0x000000076bf9e218> (a java.lang.Object)
- locked <0x000000076bf9e208> (a java.lang.Object)
at com.test.demo.DeadLockTest$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001d9e6800 nid=0x63e0 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001d916800 nid=0x5e00 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001d913800 nid=0x4b94 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001d913000 nid=0xd34 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001d910000 nid=0x3908 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001d908800 nid=0x1ec8 runnable [0x000000001eb2e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076be84c68> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076be84c68> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001d824000 nid=0x3ff4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001d823000 nid=0x80bc runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001d7d8000 nid=0x617c in Object.wait() [0x000000001e82e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076bd08ed8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076bd08ed8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001c45e000 nid=0x9c in Object.wait() [0x000000001e72f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076bd06c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076bd06c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001c456000 nid=0x9024 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002a49000 nid=0x5708 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002a4a800 nid=0x8218 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002a4c000 nid=0x5044 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002a4e000 nid=0x2328 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002a51000 nid=0x753c runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002a54800 nid=0x6ac4 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002a57800 nid=0x139c runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002a59000 nid=0x6680 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001d9ec000 nid=0x6fb0 waiting on condition
JNI global references: 317
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000001d7d3c48 (object 0x000000076bf9e208, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001d7d27a8 (object 0x000000076bf9e218, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.test.demo.DeadLockTest.lambda$main$1(DeadLockTest.java:32)
- waiting to lock <0x000000076bf9e208> (a java.lang.Object)
- locked <0x000000076bf9e218> (a java.lang.Object)
at com.test.demo.DeadLockTest$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at com.test.demo.DeadLockTest.lambda$main$0(DeadLockTest.java:19)
- waiting to lock <0x000000076bf9e218> (a java.lang.Object)
- locked <0x000000076bf9e208> (a java.lang.Object)
at com.test.demo.DeadLockTest$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.