JVM核心知识1
关于 JVM 的核心基础知识
- 我们要知道Java是一个跨平台的语言,它为什么能够做到跨平台?是因为首先它是使用的class文件,在多个平台上都可以运行。为什么可以在多个平台运行?因为只需在各个平台上安装了JVM虚拟机的环境,就可以运行我们的Java程序。而对于C代码以及C++等其他语言的代码,它们都需要在对应的操作系统环境上有相应的文件,比如说C语言就会有.dll文件或者.so文件。在实际应用中,比如Java需要引用一些C语言的库的话,涉及这些语言就变得复杂,特别是在构建过程中尤其麻烦。
- 是JVM帮我们极大地简化了内存管理的操作。我们在编写代码时无需考虑内存管理问题。如果我们遇到内存方面的问题,只需使用JVM提供的工具排查内存管理问题,并通过修改内存参数来优化我们的Java内存管理。整个过程变得轻松而简单。
# 字节码技术
每个点 class 文件都是一些二进制的代码,这些代码里面包含了
程序的运行是基于 jvm 上面的战争里面的线程运行的。
当里面的线程进行工作时,也就是程序运行时,每调用一个新的方法。那么当前的站上的线程就会去开辟一个新的战争。每个战争上面都会包含操作数栈、局部变量,以及指向常量池的 class 引用
# JVM 类加载器
类加载器也就是从文件系统中加载 class 文件到 JVM 虚拟机里面的一个加载器。
类加载器的过程是从外部文件系统中加载 class 文件到 JVM 虚拟机里面。首先,第一步是加载,就是找到 class 文件。第二步是找到 class 文件之后,需要对这个 class 文件里的内容进行验证。
当验证完成之后,需要对里面的一些变量进行准备。准备完成之后,会将里面的引用变量指向它们真实的引用。接下来是解析,对符号引用进行解析为直接引用。然后是初始化,也就是执行构造器、静态变量赋值和静态代码块的执行。最后是使用,然后卸载。
# 类的加载时机
# 分析生产环境JVM启动参数
jps -lvm
列出 -XX 开头的参数:
-XX:ErrorFile=C:\Users\willo\java_error_in_idea64_%p.log
-XX:HeapDumpPath=C:\Users\willo\java_error_in_idea64.hprof
-XX:ReservedCodeCacheSize=512m // 为JIT编译器保留的代码缓存大小为512MB。
-XX:+UseG1GC
-XX:SoftRefLRUPolicyMSPerMB=50
-XX:CICompilerCount=2 //设置JIT编译器线程数。
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow //在快速抛出异常时不省略堆栈信息。
-XX:+IgnoreUnrecognizedVMOptions
-XX:CompileCommand=exclude,com/intellij/openapi/vfs/impl/FilePartNodeRoot,trieDescend
-XX:+HeapDumpOnOutOfMemoryError
-XX:MetaspaceSize=16m // 用于配置元空间大小和空闲空间的最小/最大比率。
-XX:MinMetaspaceFreeRatio=10
-XX:MaxMetaspaceFreeRatio=10
-XX:-ShrinkHeapInSteps
-XX:+UnlockExperimentalVMOptions
-XX:+CreateCoredumpOnCrash
-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers
-XX:+UseSerialGC // 使用串行垃圾收集器
-XX:MinHeapFreeRatio=10 // 堆空闲空间的最小和最大比率。
-XX:MaxHeapFreeRatio=10
--add-opens=java.desktop/sun.awt=ALL-UNNAMED
--add-opens=java.desktop/sun.awt.resources=ALL-UNNAMED
--add-opens=java.desktop/sun.awt.shell=ALL-UNNAMED
--add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED
-Xmx31G //最大堆(当前服务器的50%)
-Dfile.encoding=UTF-8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
java -server \
-XX:+UseConcMarkSweepGC \
-XX:CMSInitiatingOccupancyFraction=75 \
-XX:+UseCMSInitiatingOccupancyOnly \
-Xss1m \
-Dfile.encoding=UTF-8 \
-XX:-OmitStackTraceInFastThrow \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/export/xxx/log/inst-0 \
-XX:ErrorFile=logs/hs_err_pid%p.log \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintTenuringDistribution \
-XX:+PrintGCApplicationStoppedTime \
-Xloggc:logs/gc.log \
-Xmx31G \
-Xms31G
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 分析生产环境Java线程
jstack -l [进程号]
"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=365.64s tid=0x0000021b78eb6310 nid=0x5df0 waiting on condition [0x00000056e18ff000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList(java.base@17.0.5/Native Method)
at java.lang.ref.Reference.processPendingReferences(java.base@17.0.5/Reference.java:253)
at java.lang.ref.Reference$ReferenceHandler.run(java.base@17.0.5/Reference.java:215)
Locked ownable synchronizers:
- None
说明
【线程状态】
【NEW】未启动,RUNABLE在虚拟机内执行的
【BLOCKED】受阻塞的并等待监听器锁
【WATING】无限等待另外一个线程执行特定的操作
【TIMED_WATING】有期限的等待另外一个线程执行特定的操作
【TERNUBATED】已退出的
【deamon】守护进程
【prio和os_prio】分别代表线程在JVM优先级,操作系统优先级
【tid】内部线程控制结构的java内存地址(16进制)
【nid】线程ID(16进制)
【java.lang.Thread.State】线程栈信息
【Locked ownable synchronizers】可以用户定位锁依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 分析SerialGC日志
-XX:+UseSerialGC -Xms1g -Xmx1g -XX:+PrintGCDetails
GC(0) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
GC(1) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(2) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(3) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(4) DefNew: 314559K(314560K)->34944K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34944K(34944K)
GC(5) DefNew: 314560K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34944K(34944K)->34943K(34944K)
GC(6) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(7) DefNew: 314559K(314560K)->34944K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34944K(34944K)
GC(9) DefNew: 314560K(314560K)->0K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34944K(34944K)->0K(34944K)
GC(10) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
GC(11) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(12) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(14) DefNew: 314559K(314560K)->0K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->0K(34944K)
GC(15) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
GC(16) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
GC(17) DefNew: 314559K(314560K)->34944K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34944K(34944K)
GC(19) DefNew: 314560K(314560K)->0K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34944K(34944K)->0K(34944K)
GC(20) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
生成了23374个对象
Heap
def new generation total 314560K, used 81876K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000)
eden space 279616K, 16% used [0x00000000c0000000, 0x00000000c2dd50b8, 0x00000000d1110000)
from space 34944K, 99% used [0x00000000d3330000, 0x00000000d554fff8, 0x00000000d5550000)
to space 34944K, 0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000)
tenured generation total 699072K, used 449740K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000)
the space 699072K, 64% used [0x00000000d5550000, 0x00000000f0c83238, 0x00000000f0c83400, 0x0000000100000000)
Metaspace used 1025K, committed 1216K, reserved 1056768K
class space used 78K, committed 192K, reserved 1048576K
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- 平均停顿时间约为19.94毫秒,总时间是199.396毫秒。
- 生成了个对象21886
# 分析ParallelGC日志
-XX:+UseParallelGC -XX:ParallelGCThreads=7 -Xms1g -Xmx1g -XX:+PrintGCDetails
GC(0) PSYoungGen: 261681K(305664K)->43503K(305664K) Eden: 261681K(262144K)->0K(262144K) From: 0K(43520K)->43503K(43520K)
GC(1) PSYoungGen: 305647K(305664K)->43516K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43503K(43520K)->43516K(43520K)
GC(2) PSYoungGen: 305660K(305664K)->43515K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43516K(43520K)->43515K(43520K)
GC(3) PSYoungGen: 305659K(305664K)->43517K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43515K(43520K)->43517K(43520K)
GC(4) PSYoungGen: 305661K(305664K)->43517K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43517K(43520K)->43517K(43520K)
GC(5) PSYoungGen: 305661K(305664K)->43509K(160256K) Eden: 262144K(262144K)->0K(116736K) From: 43517K(43520K)->43509K(43520K)
GC(6) PSYoungGen: 160245K(160256K)->77299K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 43509K(43520K)->77299K(116224K)
GC(7) PSYoungGen: 194035K(232960K)->100339K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 77299K(116224K)->100339K(116224K)
GC(8) PSYoungGen: 217075K(232960K)->115498K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 100339K(116224K)->115498K(116224K)
GC(9) PSYoungGen: 232089K(232960K)->83965K(232960K) Eden: 116591K(116736K)->0K(116736K) From: 115498K(116224K)->83965K(116224K)
GC(10) PSYoungGen: 200526K(232960K)->45782K(232960K) Eden: 116561K(116736K)->0K(116736K) From: 83965K(116224K)->45782K(116224K)
GC(11) PSYoungGen: 45782K(232960K)->0K(232960K) Eden: 0K(116736K)->0K(116736K) From: 45782K(116224K)->0K(116224K)
GC(12) PSYoungGen: 116736K(232960K)->47628K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 0K(116224K)->47628K(116224K)
GC(13) PSYoungGen: 164364K(232960K)->40157K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 47628K(116224K)->40157K(116224K)
GC(14) PSYoungGen: 156773K(232960K)->43144K(232960K) Eden: 116616K(116736K)->0K(116736K) From: 40157K(116224K)->43144K(116224K)
GC(15) PSYoungGen: 159880K(232960K)->43502K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 43144K(116224K)->43502K(116224K)
GC(16) PSYoungGen: 160238K(232960K)->45423K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 43502K(116224K)->45423K(116224K)
GC(17) PSYoungGen: 162159K(232960K)->50166K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 45423K(116224K)->50166K(116224K)
GC(18) PSYoungGen: 166902K(232960K)->45471K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 50166K(116224K)->45471K(116224K)
GC(19) PSYoungGen: 162207K(232960K)->52582K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 45471K(116224K)->52582K(116224K)
GC(20) PSYoungGen: 169318K(232960K)->53694K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 52582K(116224K)->53694K(116224K)
GC(21) PSYoungGen: 53694K(232960K)->0K(232960K) Eden: 0K(116736K)->0K(116736K) From: 53694K(116224K)->0K(116224K)
GC(22) PSYoungGen: 116736K(232960K)->51380K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 0K(116224K)->51380K(116224K)
GC(23) PSYoungGen: 168116K(232960K)->44017K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 51380K(116224K)->44017K(116224K)
GC(24) PSYoungGen: 160727K(232960K)->48570K(232960K) Eden: 116710K(116736K)->0K(116736K) From: 44017K(116224K)->48570K(116224K)
GC(25) PSYoungGen: 165306K(232960K)->43119K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 48570K(116224K)->43119K(116224K)
GC(41) PSYoungGen: 176128K(261632K)->63185K(263680K) Eden: 176128K(176128K)->0K(178176K) From: 0K(85504K)->63185K(85504K)
GC(42) PSYoungGen: 241361K(263680K)->70238K(248832K) Eden: 178176K(178176K)->0K(178176K) From: 63185K(85504K)->70238K(70656K)
生成对象总数:27231
Heap
PSYoungGen total 248832K, used 80240K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
eden space 178176K, 5% used [0x00000000eab00000,0x00000000eb4c4570,0x00000000f5900000)
from space 70656K, 99% used [0x00000000f5900000,0x00000000f9d97ba0,0x00000000f9e00000)
to space 88064K, 0% used [0x00000000faa00000,0x00000000faa00000,0x0000000100000000)
ParOldGen total 699392K, used 391038K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
object space 699392K, 55% used [0x00000000c0000000,0x00000000d7ddfb30,0x00000000eab00000)
Metaspace used 1021K, committed 1216K, reserved 1056768K
class space used 78K, committed 192K, reserved 1048576K
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- 平均停顿时间约为 6.415 毫秒,总停顿时间约为 198.935 毫秒。
- ParallelGC和SerialGC对象分配速度和总的STW时间性能相似,但是平均STW时间相差较大
# 分析G1日志与CMS
参数
-XX:+UseConcMarkSweepGC -XX:ConcGCThreads=3 -XX:MaxGCPauseMillis=50 -Xms1g -Xmx1g -XX:+PrintGCDetails
-XX:+UseG1GC -XX:ConcGCThreads=3 -XX:MaxGCPauseMillis=50 -Xms1g -Xmx1g -XX:+PrintGCDetails -Xlog:gc+time
1
2
3
2
3
G1 :平均 GC 时间为约 2.317 毫秒
G1和CMS比较总结:
G1触发GC的频率要比CMS高(一共触发了97次),但是G1平均STW时间性能快5倍,G1最大STW时间也比CMS平均快60%。
两者内存的分配速度相差不大(前提是没有FullGC,在FullGC的场景下,G1性能还是比CMS高)
1
2
3
4
2
3
4
压测示例对比:https://github.com/hyblog/JAVA-000/blob/main/Week_01/Node7.md
# Code
package Week_01;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
public class GCLogAnalysis {
private static Random random = new Random();
public static void main(String[] args) {
long startMillis = System.currentTimeMillis();
long timeoutMillis = TimeUnit.SECONDS.toMillis(1);
long endMillis = startMillis + timeoutMillis;
LongAdder counter = new LongAdder();
int cacheSize = 2000;
Object[] cachedGarbage = new Object[cacheSize];
while (System.currentTimeMillis() < endMillis) {
Object garbage = generateGarbage(100*1024);
counter.increment();
int randomIndex = random.nextInt(2 * cacheSize);
if (randomIndex < cacheSize) {
cachedGarbage[randomIndex] = garbage;
}
}
System.out.println("生成对象总数:" + counter.longValue());
}
private static Object generateGarbage(int max) {
int randomSize = random.nextInt(max);
int type = randomSize % 4;
Object result = null;
switch (type) {
case 0:
result = new int[randomSize];
break;
case 1:
result = new byte[randomSize];
break;
case 2:
result = new double[randomSize];
break;
default:
StringBuilder builder = new StringBuilder();
String randomString = "randomString-Anything";
while (builder.length() < randomSize) {
builder.append(randomString);
builder.append(max);
builder.append(randomSize);
}
result = builder.toString();
break;
}
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56