forked from HappySnailSunshine/JavaInterview
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJVM.md
More file actions
3985 lines (2304 loc) · 141 KB
/
Copy pathJVM.md
File metadata and controls
3985 lines (2304 loc) · 141 KB
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# JVM
## 目录
[TOC]
> 主要参考自B站尚硅谷JVM课程
>
> 还有《深入理解JVM虚拟机》
# 内存与垃圾回收
- JVM和Java体系结构
- 类加载子系统
- 运行时数据区概述及线程
- 程序计数器
- 虚拟机栈
- 本地方法接口
- 本地方法栈
- 堆
- 方法区
- 直接内存
- 执行引擎
- String Table
- 垃圾回收概述
- 垃圾回收相关算法
- 垃圾回收相关概念
- 垃圾回收器
## JVM和Java体系结构
### JVM文档
https://docs.oracle.com/javase/specs/
### 各种语言的排名
世界上没有最好的语言,只有适合某种场景的最好的语言。
https://www.tiobe.com/tiobe-index/
### JVM跨语言的平台
JVM只关心,字节码文件是不是符合他的要求。
Java不是最强大的语言,但是JVM是最强大的虚拟机。

### JVM作用
JVM就是二进制字节码的运行环境。
### 特点
- 一次编译,到处运行
- 自动内存管理
- 自动垃圾回收
### JVM架构模型 (不会 计算机组成原理 汇编知识)
参考PPT 39

**重点:**
- 栈式架构可移植性好,跨平台,指令集小,指令多,执行性能比寄存器差;
- 寄存器架构完全依赖于硬件,可移植性差。
举例子,然后反编译 反编译命令 `javap -v StackStruTest.class`
```java
public class StackStruTest {
public StackStruTest() {
}
public static void main(String[] args) {
int i = 2;
int j = 3;
int var10000 = i + j;
try {
Thread.sleep(6000L);
} catch (InterruptedException var5) {
var5.printStackTrace();
}
System.out.println("hello");
}
}
```

### JVM生命周期
开始 执行 结束
`jps`命令,可以查看当前执行的进程

### JVM发展历程
Java是半解释,半编译形语言。
JIT执行编译器,参考:https://developer.ibm.com/zh/articles/j-lo-just-in-time/

热点代码(经常执行的代码),编译器会编译成字节码文件,缓存起来。
#### Sun Classic VM
最开始的时候,解释器和编译器不能同时运行。
**举例子:**
假如从A到B地点,解释器就是一直步行,得知要去B点以后,马上出发。
编译器就是坐公交,编译器编译的时候,类似等公交,可能要等的时间很长。
总结反思:
其实最好的是解释器,编译器夹杂着用,有些地方走路,有些地方很远坐公交。这也是后续编译器发展的方向。
#### Exact VM
- 热点探测
- 解释器编译器混合工作
#### Sun HotSpot VM 商用 目前属于Oracle
名称上指的是热点探测技术。
- 通过计数器找到最具有编译价值的代码,触发即时编译或者栈上替换
- 通过编译器结解释器协同工作,在最优化的程序响应时间与最佳执行性能中找到平衡。
#### BEA JRockit 商用 目前属于Oracle
专注于服务器应用。
- 所以不太注重启动的速度,所以没有解释器,只有编译器。
- 目前直接上最快的编译器。
- 后来Oracle将JRockit上面的优秀的东西整合到了HotSpot上面,在JDK8完成,在公司中JRockit团队还是占据主导,所以Java创始人,后来去了谷歌。
#### IBM J9 商用
这三个是目前世界范围内最具有影响力的三大商用虚拟机。
- 这个虚拟机只在自己服务器上面运行速度好,稳定。类似于苹果。
#### KVM 和 CDC/CLDC HotSpot
主要用于Java ME
#### Azul VM
高性能虚拟机中的战斗机。
#### Liquid VM
高性能虚拟机中的战斗机。
- 不需要操作系统就可以用,本身就可以实现专用操作系统的功能。
#### Apache Harmony
Intel和IBM联合开发的开源JVM。但是Sun坚决不让Harmony获得JCP(目前管理Java的组织)的认证。
在Java世界中没用到,结果在Android SDK中用的挺好。
#### MicroSoft JVM
微软未来在浏览器中只是Java Applets ,开发了这个。只能在wins系统用。
#### TaobaoJVM
基于OpenJDK深度定制的
- 将生命周期较长的对象移出堆外,降低GC频率。
- GCIH对象能在虚拟机中共享
#### Dalvik VM
不是遵循Java JVM规范,不能直接执行Java class文件。
主要用于Android。
#### Graal VM 未来虚拟机
2018 Oracle新发布的虚拟机。
Run Programs Faster Anywhere.
跨语言全栈虚拟机,可以作为任何语言的运行平台使用。
## 类加载子系统

- ClassLoader 只负责class文件的加载,至于它是否可以运行,则要Execution Engine决定。(就好像有人给你介绍对象,人我到来了,具体成不成就看你自己啦。你自己就是Engine)
### 类的加载过程
宏观上的加载

微观上的加载,分三个环节
#### 加载 Loading

#### 链接 Linking
##### 验证
保证被加载的正确定,不能危害自己安全。
##### 准备
给变量赋值,开始都是初始值,0值。
##### 解析
将常量池中符号引用转换为直接引用。
#### 初始化 Init

### 类加载器分类
<img src="../media/pictures/JVM.assets/image-20201119162420925.png" alt="image-20201119162420925" />
注意:
这几个不是继承关系,优先级或者说等级不一样。
实例代码:
```java
public class ClassLoaderTest {
public static void main(String[] args) {
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取其上层:扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d
//获取其上层:获取不到引导类加载器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
//对于用户自定义类来说:默认使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
}
}
```
从上到下结果:
```
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
```
总结分析:
- Bootstrap ClassLoader 相当于女王御用厨师,一般人让做个菜肯定不行啊;
- Java核心类库都是引导类加载的;
- 引导类加载器为什么看不到?是因为这个是C、C++ 写的,所以看不到。
### 虚拟机自带的加载器



### 自定义类加载

```java
/**
* 自定义用户类加载器
*/
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] result = getClassFromCustomPath(name);
if(result == null){
throw new FileNotFoundException();
}else{
return defineClass(name,result,0,result.length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
throw new ClassNotFoundException(name);
}
private byte[] getClassFromCustomPath(String name){
//从自定义路径中加载指定类:细节略
//如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作。
return null;
}
public static void main(String[] args) {
CustomClassLoader customClassLoader = new CustomClassLoader();
try {
Class<?> clazz = Class.forName("One",true,customClassLoader);
Object obj = clazz.newInstance();
System.out.println(obj.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
### ClassLoader
获取ClassLoader几种方式:

### 双亲委派机制 面试要问

举例子:
```java
//本地java.lang包下面写了个String
public class String {
//
static{
System.out.println("我是自定义的String类的静态代码块");
}
//错误: 在类 java.lang.String 中找不到 main 方法
public static void main(String[] args) {
System.out.println("hello,String");
}
}
```
```java
//其他包 引用String 发现并没有引用上面的String
public class StringTest {
public static void main(String[] args) {
java.lang.String str = new java.lang.String();
System.out.println("hello,atguigu.com");
StringTest test = new StringTest();
System.out.println(test.getClass().getClassLoader());
}
}
```
原因是:
系统类加载器加载String类的时候,会向上委派给他的上一级扩展类加载器,扩展类加载器会委派给上一级引导类加载器,引导类加载器加载的时候一看路径是Java下的lang,自己就加载系统核心类库里面的String,所以没有加载本地的String。
**举例子:** (妙不可言)
现在小孩子(系统加载器)有一个苹果,他吃之前问他妈(扩展类加载器),妈你吃吗?他妈看姥姥(引导类加载器)在身边,就问妈你吃吗?
如果姥姥吃了,就相当于直接吃啦。
如果姥姥觉得这个苹果太硬啦,说不吃啦,转手给妈妈,妈妈也觉得太酸啦,不吃。最后回到小孩子手里,只能自己吃啦。(自己加载)
#### 优势

### 沙箱安全机制
类加载器的这种委派模式就是一种沙箱安全机制,会对核心API有保护作用。
在一个隔离的环境中操作,不会污染其他东西,例如360沙箱,支付宝沙箱。
### 其他重要知识
##### 两个class对象是都为同一个类

##### 类的主动使用和被动使用

## 运行时数据区
### 概述

其中红色的是一个进程公用的,灰色的是一个线程公用的。

### 线程

#### JVM系统线程

## 程序计数器 (PC寄存器)
### PC 寄存器介绍


这个地方不会发生OOM,也没有GC。
#### 举例子


### 两个常见问题

**这里用到一个知识,并发还是并行?** 知乎大佬的回答:哈哈
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
所以我认为它们最关键的点就是:是否是『同时』

### CPU时间片

## 虚拟机栈
### 概述背景概述

#### 内存中的堆和栈

#### 虚拟机栈的基本内容


当然虚拟机栈中还要细分很多东西。

**其实也没有OOM,如果栈满了,溢出。当然也没必要GC。**

#### 设置栈内存大小
-Xss 设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。
IDEA设置虚拟机栈大小:
<img src="../media/pictures/JVM.assets/image-20201120152833067.png" alt="image-20201120152833067" style="zoom:50%;" />

### 栈的存储单位
<img src="../media/pictures/JVM.assets/image-20201120153247163.png" alt="image-20201120153247163" style="zoom:40%;" />
#### 栈运行原理

Debug的时候,可以看到栈
<img src="../media/pictures/JVM.assets/image-20201120155503215.png" alt="image-20201120155503215" style="zoom: 33%;" />


不管代码中写没写return,反编译中,都有return。
#### 栈帧内部结构
<img src="../media/pictures/JVM.assets/image-20201120161010077.png" alt="image-20201120161010077" style="zoom:50%;" />
### 栈的局部变量表
<img src="../media/pictures/JVM.assets/image-20201120161940943.png" alt="image-20201120161940943" style="zoom: 33%;" />
<img src="../media/pictures/JVM.assets/image-20201120162124503.png" alt="image-20201120162124503" style="zoom:33%;" />
```bash
cd out/production/chapter05/com/atguigu/java1/
#看字节码里面细节
javap -v LocalVariablesTest.class
```
这个里面有堆栈,参数信息等:
<img src="../media/pictures/JVM.assets/image-20201126090909485.png" alt="image-20201126090909485" style="zoom: 50%;" />
#### 用JClassLib看具体一个类的信息
LocalVariablesTest.class
<img src="../media/pictures/JVM.assets/image-20201126092238140.png" alt="image-20201126092238140" style="zoom:50%;" />
<img src="../media/pictures/JVM.assets/image-20201126092646904.png" alt="image-20201126092646904" style="zoom:50%;" />
<img src="../media/pictures/JVM.assets/image-20201126091851710.png" alt="image-20201126091851710" style="zoom:50%;" />
<img src="../media/pictures/JVM.assets/image-20201126092111443.png" alt="image-20201126092111443" style="zoom:50%;" />
分析:
- test变量,其实PC对应行号是14,这是他作用域开始的行;
- 起始PC + 长度 都是16,这个16其实就是第二张图,字节码长度16
- 局部变量最大槽数3,就是说这个方法中有三个变量,分别是args,test,num
#### 关于Slot(槽)的理解

如何理解上面最后那一句话呢?下面代码示例展示。
##### 静态方法和构造方法
代码示例():
<img src="../media/pictures/JVM.assets/image-20201126100837311.png" alt="image-20201126100837311" style="zoom: 67%;" />
```java
public static void testStatic() {
LocalVariablesTest test = new LocalVariablesTest();
Date date = new Date();
int count = 10;
System.out.println(count);
//因为this变量不存在于当前方法的局部变量表中!!
//System.out.println(this.count);
}
//关于Slot的使用的理解
public LocalVariablesTest() {
this.count = 1;
}
```
在testStatic方法中是用不了this关键字的,但是在构造方法中是可以用的。
解析:
构造方法就是初始化方法,局部变量表有他,所以可以用
<img src="../media/pictures/JVM.assets/image-20201126101157921.png" alt="image-20201126101157921" style="zoom:67%;" />
再看testStatic方法,局部变量表中没有this,肯定用不了它喽!
<img src="../media/pictures/JVM.assets/image-20201126101247753.png" alt="image-20201126101247753" style="zoom:67%;" />
##### 非静态方法
this变量就在局部变量表的第一个,代码示例:
```java
public void test1() {
Date date = new Date();
String name1 = "atguigu.com";
test2(date, name1);
System.out.println(date + name1);
}
```
<img src="../media/pictures/JVM.assets/image-20201126101903918.png" alt="image-20201126101903918" style="zoom:67%;" />
##### 各种类型,含double类型,占两个槽
```java
public String test2(Date dateP, String name2) {
dateP = null;
name2 = "songhongkang";
double weight = 130.5;//占据两个slot
char gender = '男';
return dateP + name2;
}
```
这里面其实有五个变量。但是有6个槽
<img src="../media/pictures/JVM.assets/image-20201126102744386.png" alt="image-20201126102744386" style="zoom:67%;" />
体重这个变量占了两个槽,序号是3(第四个位置),序号4(第五个位置)也被他占用啦。
<img src="../media/pictures/JVM.assets/image-20201126102833722.png" alt="image-20201126102833722" style="zoom:67%;" />
#### Slot的重复利用
```java
public void test4() {
int a = 0;
{
int b = 0;
b = a + 1;
}
//变量c使用之前已经销毁的变量b占据的slot的位置
int c = a + 1;
}
```
<img src="../media/pictures/JVM.assets/image-20201126104819057.png" alt="image-20201126104819057" style="zoom:67%;" />
在这个代码中,变量b为什么和变量c序号一样(就是占用槽其实位置一样)???
因为变量b,有效范围就是大括号之内,大括号完结,变量b原来的地方会被c重新利用。(妙啊!)
#### 静态变量和局部变量的对比
<img src="../media/pictures/JVM.assets/image-20201126105753011.png" alt="image-20201126105753011" style="zoom: 33%;" />
补充:
<img src="../media/pictures/JVM.assets/image-20201126110301456.png" alt="image-20201126110301456" style="zoom: 33%;" />
### 操作数栈
Operand Stack
栈:实现可以用数组或者链表来实现。
<img src="../media/pictures/JVM.assets/image-20201126111248667.png" alt="image-20201126111248667" style="zoom: 33%;" />
<img src="../media/pictures/JVM.assets/image-20201126111916720.png" alt="image-20201126111916720" style="zoom: 33%;" />
<img src="../media/pictures/JVM.assets/image-20201126112309105.png" alt="image-20201126112309105" style="zoom:33%;" />
### 代码追踪
具体看PPT,求和运算。
### 栈顶缓存技术

### 动态链接

动态链接是栈帧里面的一个部分。
动态链接指向运行时常量池的方法引用。


示例代码
```java
public class DynamicLinkingTest {
int num = 10;
public DynamicLinkingTest() {
}
public void methodA() {
System.out.println("methodA()....");
}
public void methodB() {
System.out.println("methodB()....");
this.methodA();
++this.num;
}
}
```
#### 字节码文件中的常量池
<img src="../media/pictures/JVM.assets/image-20201126140049958.png" alt="image-20201126140049958" style="zoom: 67%;" />
字节码中的方法
<img src="../media/pictures/JVM.assets/image-20201126140238771.png" alt="image-20201126140238771" style="zoom:67%;" />
这里面的#7,和#2就是指的上面常量池里面的值。
**注意:**
在加载的时候,会把这个类需要用到的东西,都加载进来,以一个符号的形式表示。
例如:#13 表示返回值为空的,下面还有输出流等。
**为什么会用这个动态链接呢?为什么不把内容直接拿过来用呢?**
Java里面的好多其实都是引用,之所以用引用一来节省空间,二来好多内容可以共享。而且好多东西很大,不能都保存在字节码文件中,所以用引用的方式。
### 方法的调用
<img src="../media/pictures/JVM.assets/image-20201126150334306.png" alt="image-20201126150334306" style="zoom: 33%;" />
<img src="../media/pictures/JVM.assets/image-20201126151820734.png" alt="image-20201126151820734" style="zoom: 33%;" />
面向过程的语言,大多数在编译器就可以确定,所以是早期绑定,面向对象语言,早期晚期绑定都有。
<img src="../media/pictures/JVM.assets/image-20201126152941124.png" alt="image-20201126152941124" style="zoom:33%;" />
#### 虚方法和非虚方法
<img src="../media/pictures/JVM.assets/image-20201207090534235.png" alt="image-20201207090534235" style="zoom: 33%;" />
子类对象多态的使用前提:
- 类的继承
- 方法的重写
<img src="../media/pictures/JVM.assets/image-20201207091021163.png" alt="image-20201207091021163" style="zoom:33%;" />
#### invokedynamic指令
<img src="../media/pictures/JVM.assets/image-20201207171617856.png" alt="image-20201207171617856" style="zoom:33%;" />
##### 动态类型语言和静态类型语言
<img src="../media/pictures/JVM.assets/image-20201207171940805.png" alt="image-20201207171940805" style="zoom:33%;" />
Java其实是动静态类型语言(对于类型的检查在编译器)。Js,Python 动态类型语言(对类型的检查在运行期)。
<img src="../media/pictures/JVM.assets/image-20201207172219469.png" alt="image-20201207172219469" style="zoom: 50%;" />
**这个的增加就是为了是某些动态语言,可以运行到JVM虚拟机上面。**
#### 方法重写的本质
<img src="../media/pictures/JVM.assets/image-20201207174642203.png" alt="image-20201207174642203" style="zoom:33%;" />
方法重写本质依次往上找,不太靠谱,所以有下面的需方法表。
#### 虚方法表
<img src="../media/pictures/JVM.assets/image-20201208090140932.png" alt="image-20201208090140932" style="zoom:33%;" />
<img src="../media/pictures/JVM.assets/image-20201208090205213.png" alt="image-20201208090205213" style="zoom:33%;" />
说明:下面白色的方法是Son类重写父类Father的方法,蓝色的方法是Object类的方法。如果子类Son调用toString方法,那么虚方法表回指向Object类的toString方法,直接调用Object类中的方法,不用再一层一层往上面找。
虚方法表举例:
```java
/**
* 虚方法表的举例
*
* @author shkstart
* @create 2020 下午 1:11