Java性能调优面试-2
1、JVM整体架构
2、JVM内存模型
1、类装载子系统将字节码文件【.class】装载到运行时数据区,即装载到方法区;
2、执行引擎负责执行
3、JVM内存模型分析
1、线程私有:线程栈、本地方法栈以及程序计数器。
线程公有:堆、方法区【元空间】
2、一个方法对应一块栈帧内存区域,栈帧是存放在栈区间内部
下图中main()先进栈,computer()后进栈。
3、栈帧内部包含:局部变量表、操作数栈、动态链接、方法出口
public class Math {
public static final Integer CONSTANT = 666;
public int compute(){ //一个方法对应一块栈帧内存区域
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = new Math();
math.compute();
Math math2 = new Math();
math.compute();
System.out.println("hello");
}
}
4、生成类的字节码文件【javap -c Math.class > Math.txt】
操作数栈:变量值中转的内存区域。
iconst_1:将int类型变量1压入操作数栈【即操作数栈中有一个值1】
istore_1 : 将int类型值存入局部变量1【即局部变量表中有一个a=1】
iconst_2:将int类型变量2压入操作数栈【即操作数栈中有一个值2】
istore_2 : 将int类型值存入局部变量2【即局部变量表中有一个b=2】
iload_1:从局部变量1中装载int类型值【即将局部变量表中a=1的1装载到操作数栈】
iload_2:从局部变量2中装载int类型值【即将局部变量表中b=2的2装载到操作数栈】
iadd:从操作数栈的栈顶弹出两个变量做加法,对结果压入操作数栈【3】
bipush: 将一个8位带符号整数压入栈【将10压入操作数栈】
imul: 从操作数栈的栈顶弹出两个变量做乘法,对结果压入操作数栈【30】
istore_3:将int类型变量30压入操作数栈【即操作数栈中有一个值30】
iload_3:将int类型值存入局部变量30【即局部变量表中有一个c=30】
ireturn:
动态链接:在main方法中,math.compute()中compute属于静态符号,在程序执行中将符号转为指令码,进行执行。
动态链接:在程序动态运行时,存储指令码的内存地址。
方法出口:compute()执行完后,执行main方法的位置。
程序计数器:保存下一条要执行指令的地址。
本地方法栈:private native void start0()
为什么存在本地方法栈?因为java是95年产生,之前一直是c,java为了与c语言交互,故通过本地方法,java执行c代码.dll。
现在跨语言交互:Thrift、http、微服务等。
命令:javap -c Math.class > Math.txt
Compiled from "Math.java"
public class effective.java.Math {
public static final java.lang.Integer CONSTANT;
public effective.java.Math();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int compute();
Code:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
public static void main(java.lang.String[]);
Code:
0: new #2 // class effective/java/Math
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method compute:()I
12: pop
13: return
static {};
Code:
0: sipush 666
3: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: putstatic #6 // Field CONSTANT:Ljava/lang/Integer;
9: return
}
命令:javap -v Math.class > Maths.txt
Classfile /Users/lihe/workspace/crane/target/classes/effective/java/Math.class
Last modified 2021-1-17; size 759 bytes
MD5 checksum 64696f7de671d6425ce394fda905f460
Compiled from "Math.java"
public class effective.java.Math
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#31 // java/lang/Object."<init>":()V
#2 = Class #32 // effective/java/Math
#3 = Methodref #2.#31 // effective/java/Math."<init>":()V
#4 = Methodref #2.#33 // effective/java/Math.compute:()I
#5 = Methodref #34.#35 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#6 = Fieldref #2.#36 // effective/java/Math.CONSTANT:Ljava/lang/Integer;
#7 = Class #37 // java/lang/Object
#8 = Utf8 CONSTANT
#9 = Utf8 Ljava/lang/Integer;
#10 = Utf8 <init>
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 Leffective/java/Math;
#17 = Utf8 compute
#18 = Utf8 ()I
#19 = Utf8 a
#20 = Utf8 I
#21 = Utf8 b
#22 = Utf8 c
#23 = Utf8 main
#24 = Utf8 ([Ljava/lang/String;)V
#25 = Utf8 args
#26 = Utf8 [Ljava/lang/String;
#27 = Utf8 math
#28 = Utf8 <clinit>
#29 = Utf8 SourceFile
#30 = Utf8 Math.java
#31 = NameAndType #10:#11 // "<init>":()V
#32 = Utf8 effective/java/Math
#33 = NameAndType #17:#18 // compute:()I
#34 = Class #38 // java/lang/Integer
#35 = NameAndType #39:#40 // valueOf:(I)Ljava/lang/Integer;
#36 = NameAndType #8:#9 // CONSTANT:Ljava/lang/Integer;
#37 = Utf8 java/lang/Object
#38 = Utf8 java/lang/Integer
#39 = Utf8 valueOf
#40 = Utf8 (I)Ljava/lang/Integer;
{
public static final java.lang.Integer CONSTANT;
descriptor: Ljava/lang/Integer;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
public effective.java.Math();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Leffective/java/Math;
public int compute();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
LineNumberTable:
line 14: 0
line 15: 2
line 16: 4
line 17: 11
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 this Leffective/java/Math;
2 11 1 a I
4 9 2 b I
11 2 3 c I
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class effective/java/Math
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method compute:()I
12: pop
13: return
LineNumberTable:
line 21: 0
line 22: 8
line 23: 13
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 args [Ljava/lang/String;
8 6 1 math Leffective/java/Math;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: sipush 666
3: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: putstatic #6 // Field CONSTANT:Ljava/lang/Integer;
9: return
LineNumberTable:
line 11: 0
}
SourceFile: "Math.java"
5、JVM各部分
1、栈与堆关系?栈中对象变量执行堆中具体new的对象。
2、方法区【元空间】包含常量、静态变量、类元信息
3、方法区与堆关系?是相互指向。方法区中的静态变量如果是一个对象会指向堆中的对象;对中对象会指向方法区中的类元信息。
3、堆中new 对象是Object
Object Header(对象头):对象头中有个头指针指向对象所属的类。
对象头中头指针作用:当Math msth = new Math();math.compute()时,执行compute()时,对象通过头指针找到对应的类信息,找到类中方法进行执行。
6、堆区间
1、new 出的对象存储在eden。空间分配 Eden【8/10】,From【1/10】,To【1/10】,老年代【1/3】
2、当主线程执行完成,即main()执行完,即mian()栈帧出栈,此时堆中对象还存在,需要GC
3、GC Roots根节点:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等。
4、对象头中含有分代年龄字段,
老年代对象:静态变量和数据库连接池。其中正常对象年龄达到15岁后自动放到老年代。
7、JVM调优工具--jvisualvm
1、Eden Space从0增长到最高处,代表EdenSpace区被对象占满,此时GCTime进行GC。此时Eden Space空从0开始。
2、当Eden Space满发生GC时,suvivor区存储活着的对象年龄+1,此时在Suvicor0和Suvivor1不断移动
3、当对象年龄=15时,对象会移动到老年代,故老年代时突然升高。
4、当old区满时,发生Full GC。如果次时老年代中的对象都有指针指向,都不是垃圾,则会导致old不会释放空间,则发生OOM
public class HelpTest {
byte[] a = new byte[1024*100];
public static void main(String[] args) throws InterruptedException {
List<HelpTest> helpTests = new ArrayList<>();
while(true){
helpTests.add(new HelpTest());
Thread.sleep(50);
}
}
}
5、JVM调优
1、调优目的:因为实际FullGC非常慢,期间有STW会导致程序卡住,性能下降。目的是1、减少FullGC发生次数;2、减少一次FullGC时间
2、JVM调优主要是调整下面两个指标:
停顿时间:垃圾收集器做垃圾回收中断应用执行的时间。-XX:MaxGCPauseMillis
吞吐量:垃圾收集的时间和总时间占比:1/(1+n),吞吐量为1-1/(1+n)。-XX:GCTimeRate
卑微的小丑之kill: 确实不错
m0_73444243: 你好,请问最后是怎么解决的呢,我也碰到这个问题了
souuuuuux: 总结的太棒了,谢谢!一直有点概念混淆,终于捋顺了
suyujun01: 强,这个问题折腾我差不多一天!
yygr: 我的理解是符号引用就是一个字符串的表达式,就是真实物体的代表名称,而直接引用就是真正的物体,比如new出来的对象