文章目录
- 前言
- 一、🚗 双亲委派机制
- 1、 问题的引出:是否会被外来程序对系统进行破坏
- 2、总结
- 3、双亲委派的优势
- 4、沙箱安全机制
- 5、其他
- 二、🚒 运行时数据区
- 线程
- 三、🛺 PC 寄存器
- 概述(记录下一条程序指令的地址)
- 举例说明
- 两个常见的问题
- CPU的时间片
- 四、🚈 Java虚拟机栈
- 1、虚拟机栈的基本内容
- 虚拟机栈出现的背景
- 虚拟机栈的基本内容
- 虚拟机栈可能出现的异常
- 设置栈的大小 -Xss
- 2、栈的存储单位
- 栈中存储什么
- 栈的运行原理
- 栈帧的组成结构
- 3、局部变量表(当前方法有效)
- 字节码中方法内部的剖析
- 关于Slot 的理解(变量槽)
- 变量槽的重复利用
- 4、静态变量与局部变量的对比
- 5、操作数栈 (局部变量表和操作数栈都是数组实现)
- 代码追踪
- 6、栈顶缓存技术(了解即可)
- 7、动态连接
- 8、方法的调用
- 9、虚方法与非虚方法
- 方法重写的本质
- 10、方法返回地址
- 11、一些附加信息
- 12、虚拟机栈相关面试题
前言
一、🚗 双亲委派机制
1、 问题的引出:是否会被外来程序对系统进行破坏
现在我们自己定义了一个 java.lang.String 的Java类,当我们去 new String 的时候会不会去加载这个自定义的String 呢? 显然是不会的,因为如果随意一个类都能被加载到我们的系统中,如果有人恶意的植入一些代码,我们的系统就崩掉了。
public class String {
//
static{
System.out.println("我是自定义的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());
}
}
2、总结
双亲委派机制,就是先让父类加载器进行类的加载,父类不能进行加载时子类进行加载,比如上面自定义的String 类,要进行类加载,会先从 ApplicationClassloader 依次向上委托,到 BootStrapClassLoader ,这时引导类加载器发现是 java.lang.String 包下的类,自己可以加载,就回去加载 JDK 中的String ,而不会加载我们自定义的 String 类。
后面我们又创建了一个 StringTest 的测试类,并获取到了 classLoader 为 AppClassLoader 。他的加载流程是,先从App 到 bootStrap ,bootStrap 发现不归自己管,又会向下委托给扩展类加载器,然后继续向下委托给 AppClassLoader。
加载 Java的核心类库 rt.jar 会使用引导类加载器,但是当要加载 rt.jar 里面接口的第三方实现子类JDBC,会反向委托给 线程上下文加载器(默认使用 AppClassLoader)进行加载。
3、双亲委派的优势
4、沙箱安全机制
5、其他
二、🚒 运行时数据区
线程
三、🛺 PC 寄存器
概述(记录下一条程序指令的地址)
记录下一条程序指令的地址
举例说明
对应的字节码文件
package com.atguigu.java;
/**
* @author shkstart
* @create 2020 下午 6:46
*/
public class PCRegisterTest {
public static void main(String[] args) {
int i = 10;
int j = 20;
int k = i + j;
String s = "abc";
System.out.println(i);
System.out.println(k);
}
}
Classfile /G:/Java/JVM/上篇/代码/JVMDemo/out/production/chapter04/com/atguigu/java/PCRegisterTest.class
Last modified 2020-1-14; size 675 bytes
MD5 checksum 53b3ef104479ec9e9b7ce5319e5881d3
Compiled from "PCRegisterTest.java"
public class com.atguigu.java.PCRegisterTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#26 // java/lang/Object."<init>":()V
#2 = String #27 // abc
#3 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
#5 = Class #32 // com/atguigu/java/PCRegisterTest
#6 = Class #33 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/atguigu/java/PCRegisterTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 i
#19 = Utf8 I
#20 = Utf8 j
#21 = Utf8 k
#22 = Utf8 s
#23 = Utf8 Ljava/lang/String;
#24 = Utf8 SourceFile
#25 = Utf8 PCRegisterTest.java
#26 = NameAndType #7:#8 // "<init>":()V
#27 = Utf8 abc
#28 = Class #34 // java/lang/System
#29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
#30 = Class #37 // java/io/PrintStream
#31 = NameAndType #38:#39 // println:(I)V
#32 = Utf8 com/atguigu/java/PCRegisterTest
#33 = Utf8 java/lang/Object
#34 = Utf8 java/lang/System
#35 = Utf8 out
#36 = Utf8 Ljava/io/PrintStream;
#37 = Utf8 java/io/PrintStream
#38 = Utf8 println
#39 = Utf8 (I)V
{
public com.atguigu.java.PCRegisterTest();
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 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/atguigu/java/PCRegisterTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: bipush 10
2: istore_1
3: bipush 20
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: ldc #2 // String abc
12: astore 4
14: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_1
18: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
21: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_3
25: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
28: return
LineNumberTable:
line 10: 0
line 11: 3
line 12: 6
line 14: 10
line 15: 14
line 16: 21
line 18: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
3 26 1 i I
6 23 2 j I
10 19 3 k I
14 15 4 s Ljava/lang/String;
}
SourceFile: "PCRegisterTest.java"
两个常见的问题
CPU的时间片
四、🚈 Java虚拟机栈
1、虚拟机栈的基本内容
虚拟机栈出现的背景
虚拟机栈的基本内容
栈 存在OOM 内存溢出,但是不存在GC 垃圾回收,因为只有进栈和出栈的操作,所以不存在垃圾回收
虚拟机栈可能出现的异常
Exception in thread “main” java.lang.StackOverflowError
package com.atguigu.java;
/**
* 演示栈中的异常:StackOverflowError
* @author shkstart
* @create 2020 下午 9:08
*
* 默认情况下:count : 11420
* 设置栈的大小: -Xss256k : count : 2465
*/
public class StackErrorTest {
private static int count = 1;
public static void main(String[] args) {
System.out.println(count);
count++;
main(args);
}
}
设置栈的大小 -Xss
2、栈的存储单位
栈中存储什么
栈的运行原理
栈帧的组成结构
3、局部变量表(当前方法有效)
字节码中方法内部的剖析
这里主要看main 方法
关于Slot 的理解(变量槽)
变量槽的重复利用
4、静态变量与局部变量的对比
5、操作数栈 (局部变量表和操作数栈都是数组实现)
代码追踪
bipush 是将byte转换为 int 类型
istore 是存入局部变量表的位置索引,从 1就开始是因为 0是一个 this
iload 是取出局部变量表的数据
iadd 是做求和操作
6、栈顶缓存技术(了解即可)
7、动态连接
8、方法的调用
package com.atguigu.java2;
/**
* 说明早期绑定和晚期绑定的例子
* @author shkstart
* @create 2020 上午 11:59
*/
class Animal{
public void eat(){
System.out.println("动物进食");
}
}
interface Huntable{
void hunt();
}
class Dog extends Animal implements Huntable{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void hunt() {
System.out.println("捕食耗子,多管闲事");
}
}
class Cat extends Animal implements Huntable{
public Cat(){
super();//表现为:早期绑定
}
public Cat(String name){
this();//表现为:早期绑定
}
@Override
public void eat() {
super.eat();//表现为:早期绑定
System.out.println("猫吃鱼");
}
@Override
public void hunt() {
System.out.println("捕食耗子,天经地义");
}
}
public class AnimalTest {
public void showAnimal(Animal animal){
animal.eat();//表现为:晚期绑定
}
public void showHunt(Huntable h){
h.hunt();//表现为:晚期绑定
}
}
虚函数就是 父类型引用指向子类型对象方法。
9、虚方法与非虚方法
package com.atguigu.java2;
/**
* 解析调用中非虚方法、虚方法的测试
*
* invokestatic指令和invokespecial指令调用的方法称为非虚方法
* @author shkstart
* @create 2020 下午 12:07
*/
class Father {
public Father() {
System.out.println("father的构造器");
}
public static void showStatic(String str) {
System.out.println("father " + str);
}
public final void showFinal() {
System.out.println("father show final");
}
public void showCommon() {
System.out.println("father 普通方法");
}
}
public class Son extends Father {
public Son() {
//invokespecial
super();
}
public Son(int age) {
//invokespecial
this();
}
//不是重写的父类的静态方法,因为静态方法不能被重写!
public static void showStatic(String str) {
System.out.println("son " + str);
}
private void showPrivate(String str) {
System.out.println("son private" + str);
}
public void show() {
//invokestatic
showStatic("atguigu.com");
//invokestatic
super.showStatic("good!");
//invokespecial
showPrivate("hello!");
//invokespecial
super.showCommon();
//invokevirtual
showFinal();//因为此方法声明有final,不能被子类重写,所以也认为此方法是非虚方法。
//虚方法如下:
//invokevirtual
showCommon();
info();
MethodInterface in = null;
//invokeinterface
in.methodA();
}
public void info(){
}
public void display(Father f){
f.showCommon();
}
public static void main(String[] args) {
Son so = new Son();
so.show();
}
}
interface MethodInterface{
void methodA();
}
方法重写的本质
package com.atguigu.java3;
/**
* 虚方法表的举例
*
* @author shkstart
* @create 2020 下午 1:11
*/
interface Friendly {
void sayHello();
void sayGoodbye();
}
class Dog {
public void sayHello() {
}
public String toString() {
return "Dog";
}
}
class Cat implements Friendly {
public void eat() {
}
public void sayHello() {
}
public void sayGoodbye() {
}
protected void finalize() {
}
public String toString(){
return "Cat";
}
}
class CockerSpaniel extends Dog implements Friendly {
public void sayHello() {
super.sayHello();
}
public void sayGoodbye() {
}
}
public class VirtualMethodTable {
}
10、方法返回地址
保存进入方法的指令地址,在方法结束后返回这个地址,执行后续代码
package com.atguigu.java3;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
/**
*
* 返回指令包含ireturn(当返回值是boolean、byte、char、short和int类型时使用)、
* lreturn、freturn、dreturn以及areturn,另外还有一个return指令供声明为void的方法、
* 实例初始化方法、类和接口的初始化方法使用。
*
* @author shkstart
* @create 2020 下午 4:05
*/
public class ReturnAddressTest {
public boolean methodBoolean() {
return false;
}
public byte methodByte() {
return 0;
}
public short methodShort() {
return 0;
}
public char methodChar() {
return 'a';
}
public int methodInt() {
return 0;
}
public long methodLong() {
return 0L;
}
public float methodFloat() {
return 0.0f;
}
public double methodDouble() {
return 0.0;
}
public String methodString() {
return null;
}
public Date methodDate() {
return null;
}
public void methodVoid() {
}
static {
int i = 10;
}
//
public void method2() {
methodVoid();
try {
method1();
} catch (IOException e) {
e.printStackTrace();
}
}
public void method1() throws IOException {
FileReader fis = new FileReader("atguigu.txt");
char[] cBuffer = new char[1024];
int len;
while ((len = fis.read(cBuffer)) != -1) {
String str = new String(cBuffer, 0, len);
System.out.println(str);
}
fis.close();
}
}
11、一些附加信息
12、虚拟机栈相关面试题
package com.atguigu.java3;
/**
* 面试题:
* 方法中定义的局部变量是否线程安全?具体情况具体分析
*
* 何为线程安全?
* 如果只有一个线程才可以操作此数据,则必是线程安全的。
* 如果有多个线程操作此数据,则此数据是共享数据。如果不考虑同步机制的话,会存在线程安全问题。
* @author shkstart
* @create 2020 下午 7:48
*/
public class StringBuilderTest {
int num = 10;
//s1的声明方式是线程安全的
public static void method1(){
//StringBuilder:线程不安全
StringBuilder s1 = new StringBuilder();
s1.append("a");
s1.append("b");
//...
}
//sBuilder的操作过程:是线程不安全的
public static void method2(StringBuilder sBuilder){
sBuilder.append("a");
sBuilder.append("b");
//...
}
//s1的操作:是线程不安全的
public static StringBuilder method3(){
StringBuilder s1 = new StringBuilder();
s1.append("a");
s1.append("b");
return s1;
}
//s1的操作:是线程安全的
public static String method4(){
StringBuilder s1 = new StringBuilder();
s1.append("a");
s1.append("b");
return s1.toString();
}
public static void main(String[] args) {
StringBuilder s = new StringBuilder();
new Thread(() -> {
s.append("a");
s.append("b");
}).start();
method2(s);
}
}