第四章 运行时数据区


文章目录

  • 前言
  • 一、 双亲委派机制
    • 1、 问题的引出:是否会被外来程序对系统进行破坏
    • 2、总结
    • 3、双亲委派的优势
    • 4、沙箱安全机制
    • 5、其他
  • 二、 运行时数据区
    • 线程
  • 三、 PC 寄存器
    • 概述(记录下一条程序指令的地址)
    • 举例说明
    • 两个常见的问题
    • CPU的时间片
  • 四、 Java虚拟机栈
    • 1、虚拟机栈的基本内容
      • 虚拟机栈出现的背景
      • 虚拟机栈的基本内容
      • 虚拟机栈可能出现的异常
      • 设置栈的大小 -Xss
    • 2、栈的存储单位
      • 栈中存储什么
      • 栈的运行原理
      • 栈帧的组成结构
    • 3、局部变量表(当前方法有效)
      • 字节码中方法内部的剖析
      • 关于Slot 的理解(变量槽)
      • 变量槽的重复利用
    • 4、静态变量与局部变量的对比
    • 5、操作数栈 (局部变量表和操作数栈都是数组实现)
      • 代码追踪
    • 6、栈顶缓存技术(了解即可)
    • 7、动态连接
    • 8、方法的调用
    • 9、虚方法与非虚方法
      • 方法重写的本质
    • 10、方法返回地址
    • 11、一些附加信息
    • 12、虚拟机栈相关面试题


前言


一、 双亲委派机制

第四章 运行时数据区_第1张图片

1、 问题的引出:是否会被外来程序对系统进行破坏

现在我们自己定义了一个 java.lang.String 的Java类,当我们去 new String 的时候会不会去加载这个自定义的String 呢? 显然是不会的,因为如果随意一个类都能被加载到我们的系统中,如果有人恶意的植入一些代码,我们的系统就崩掉了。

第四章 运行时数据区_第2张图片

第四章 运行时数据区_第3张图片

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、总结

第四章 运行时数据区_第4张图片

双亲委派机制,就是先让父类加载器进行类的加载,父类不能进行加载时子类进行加载,比如上面自定义的String 类,要进行类加载,会先从 ApplicationClassloader 依次向上委托,到 BootStrapClassLoader ,这时引导类加载器发现是 java.lang.String 包下的类,自己可以加载,就回去加载 JDK 中的String ,而不会加载我们自定义的 String 类。

后面我们又创建了一个 StringTest 的测试类,并获取到了 classLoader 为 AppClassLoader 。他的加载流程是,先从App 到 bootStrap ,bootStrap 发现不归自己管,又会向下委托给扩展类加载器,然后继续向下委托给 AppClassLoader。

第四章 运行时数据区_第5张图片

加载 Java的核心类库 rt.jar 会使用引导类加载器,但是当要加载 rt.jar 里面接口的第三方实现子类JDBC,会反向委托给 线程上下文加载器(默认使用 AppClassLoader)进行加载。

第四章 运行时数据区_第6张图片

3、双亲委派的优势

第四章 运行时数据区_第7张图片

4、沙箱安全机制

第四章 运行时数据区_第8张图片

5、其他

第四章 运行时数据区_第9张图片
第四章 运行时数据区_第10张图片
第四章 运行时数据区_第11张图片
第四章 运行时数据区_第12张图片

二、 运行时数据区

第四章 运行时数据区_第13张图片
第四章 运行时数据区_第14张图片
第四章 运行时数据区_第15张图片
第四章 运行时数据区_第16张图片

线程

第四章 运行时数据区_第17张图片
第四章 运行时数据区_第18张图片

三、 PC 寄存器

概述(记录下一条程序指令的地址)

记录下一条程序指令的地址

第四章 运行时数据区_第19张图片
第四章 运行时数据区_第20张图片
第四章 运行时数据区_第21张图片
第四章 运行时数据区_第22张图片

举例说明

第四章 运行时数据区_第23张图片

对应的字节码文件

第四章 运行时数据区_第24张图片

第四章 运行时数据区_第25张图片

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."":()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          // "":()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."":()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"

两个常见的问题

第四章 运行时数据区_第26张图片
第四章 运行时数据区_第27张图片

CPU的时间片

第四章 运行时数据区_第28张图片

四、 Java虚拟机栈

1、虚拟机栈的基本内容

虚拟机栈出现的背景

第四章 运行时数据区_第29张图片
第四章 运行时数据区_第30张图片
第四章 运行时数据区_第31张图片

虚拟机栈的基本内容

第四章 运行时数据区_第32张图片

栈 存在OOM 内存溢出,但是不存在GC 垃圾回收,因为只有进栈和出栈的操作,所以不存在垃圾回收

第四章 运行时数据区_第33张图片

虚拟机栈可能出现的异常

第四章 运行时数据区_第34张图片

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);
    }

}

第四章 运行时数据区_第35张图片

设置栈的大小 -Xss

第四章 运行时数据区_第36张图片

第四章 运行时数据区_第37张图片
第四章 运行时数据区_第38张图片

2、栈的存储单位

栈中存储什么

第四章 运行时数据区_第39张图片

栈的运行原理

第四章 运行时数据区_第40张图片
第四章 运行时数据区_第41张图片
第四章 运行时数据区_第42张图片

栈帧的组成结构

第四章 运行时数据区_第43张图片

第四章 运行时数据区_第44张图片
第四章 运行时数据区_第45张图片

3、局部变量表(当前方法有效)

第四章 运行时数据区_第46张图片
第四章 运行时数据区_第47张图片

字节码中方法内部的剖析

这里主要看main 方法

第四章 运行时数据区_第48张图片

第四章 运行时数据区_第49张图片
第四章 运行时数据区_第50张图片

第四章 运行时数据区_第51张图片
第四章 运行时数据区_第52张图片
第四章 运行时数据区_第53张图片
第四章 运行时数据区_第54张图片

第四章 运行时数据区_第55张图片

关于Slot 的理解(变量槽)

第四章 运行时数据区_第56张图片
第四章 运行时数据区_第57张图片
第四章 运行时数据区_第58张图片

变量槽的重复利用

第四章 运行时数据区_第59张图片
第四章 运行时数据区_第60张图片

4、静态变量与局部变量的对比

第四章 运行时数据区_第61张图片

第四章 运行时数据区_第62张图片

第四章 运行时数据区_第63张图片

5、操作数栈 (局部变量表和操作数栈都是数组实现)

第四章 运行时数据区_第64张图片
第四章 运行时数据区_第65张图片
第四章 运行时数据区_第66张图片
第四章 运行时数据区_第67张图片
第四章 运行时数据区_第68张图片

代码追踪

第四章 运行时数据区_第69张图片

bipush 是将byte转换为 int 类型
istore 是存入局部变量表的位置索引,从 1就开始是因为 0是一个 this
iload 是取出局部变量表的数据
iadd 是做求和操作

6、栈顶缓存技术(了解即可)

第四章 运行时数据区_第70张图片

7、动态连接

第四章 运行时数据区_第71张图片
第四章 运行时数据区_第72张图片
第四章 运行时数据区_第73张图片
第四章 运行时数据区_第74张图片

8、方法的调用

第四章 运行时数据区_第75张图片
第四章 运行时数据区_第76张图片


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();//表现为:晚期绑定
    }
}

第四章 运行时数据区_第77张图片

虚函数就是 父类型引用指向子类型对象方法。

9、虚方法与非虚方法

第四章 运行时数据区_第78张图片
第四章 运行时数据区_第79张图片

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();
}


第四章 运行时数据区_第80张图片
第四章 运行时数据区_第81张图片

方法重写的本质

第四章 运行时数据区_第82张图片
第四章 运行时数据区_第83张图片
第四章 运行时数据区_第84张图片
第四章 运行时数据区_第85张图片

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、方法返回地址

保存进入方法的指令地址,在方法结束后返回这个地址,执行后续代码

第四章 运行时数据区_第86张图片
第四章 运行时数据区_第87张图片

第四章 运行时数据区_第88张图片
第四章 运行时数据区_第89张图片

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、一些附加信息

第四章 运行时数据区_第90张图片

12、虚拟机栈相关面试题

第四章 运行时数据区_第91张图片

第四章 运行时数据区_第92张图片


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);

    }

}

你可能感兴趣的:(JVM,java,jvm,开发语言)