JVM_03 运行时数据区1-[程序计数器+虚拟机栈+本地方法栈]

1-运行时数据区内部结构概述

下面这张图一定要会画

阿里手册中拿到的一张图:

JIT是方法区的一部分,元空间可以理解为是方法区的一个具体实现。

关于线程间共享的说明

API中可以看到:

2-线程

JVM系统线程

3-程序计数器(PC寄存器)

1.PC Register介绍

官网:

不存在垃圾回收,不存在OOM。

2.举例说明

1)

2)

3.两个常见问题

4.CPU时间片

4-虚拟机栈

1.虚拟机栈该概述

官方:

虚拟机栈出现的背景

初步印象

内存中的栈与堆

虚拟机栈基本内容

代码:

主要特点

不存在垃圾回收,但是存在OOM。

面试题:开发中遇到的一场有哪些?

设置栈内存大小

官方文档:

2.栈的存储单位

栈中存储什么?

栈运行原理

原理图:

代码解释:

 

3.栈帧的内部结构

有些地方将方法返回地址、动态链接、一些附加信息归纳为帧数据区。

局部变量表(local variables)

1)字节码中方法内部节后的剖析

查看方式一:

操作数栈的深度为2,局部变量表的深度为3

3个变量为args、test、num

查看方式二:

java代码对应的字节码指令:

局部变量表的大小为3,字节码长度为16

java代码行号对应着字节码指令行号

变量的描述:

1)从上图可以看出11对应16,16行为test.test1();所以15行是声明16行是声明后作用的域。

2)8(初始长度)+8(有效长度)、11+5都等于16,即字节码的长度。

2)关于Slot的理解

解释32bit和64bit:

public String test2(Date dateP, String name2) {
    dateP = null;
    name2 = "songhongkang";
    double weight = 130.5;//占据两个slot
    char gender = '男';
    return dateP + name2;
}

解释:如果当前帧是由构造器或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处。

//练习:
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;
}

public void test1() {
    Date date = new Date();
    String name1 = "atguigu.com";
    test2(date, name1);
    System.out.println(date + name1);
}

1)Slot的重复利用

解释Slot重复利用:

public void test4() {
    int a = 0;
    {
        int b = 0;
        b = a + 1;
    }
        //变量c使用之前已经销毁的变量b占据的slot的位置
        int c = a + 1;
}

2)静态变量与局部变量的对比

代码:

/*
    变量的分类:按照数据类型分:① 基本数据类型  ② 引用数据类型
               按照在类中声明的位置分:① 成员变量或域信息(类中全局定义):在使用前,都经历过默认初始化赋值
                                       类变量(static修饰):linking的prepare阶段:给类变量默认赋值  ---> initial阶段:给类变量显式赋值即静态代码块赋值
                                       实例变量(无static修饰):随着对象的创建,会在堆空间中分配实例变量空间,并进行默认赋值
                                     ② 局部变量(方法体中):在使用前,必须要进行显式赋值的!否则,编译不通过
*/

public void test5Temp(){
    int num;
    //System.out.println(num);//错误信息:变量num未进行初始化
}

操作数栈(Operand Stack)

可以使用数组和链表来实现

数组一旦创建,其长度是确定的

代码追踪(字节码指令执行分析)

注意:byte、short、char、boolean都是以int类型来保存

具体解析:

常见面试题 i++和++i 的区别

1)

计算i:

字节码文件:

计算j:

计算k:

2)

public class OperandStackTest {
    public void testAddOperation() {
        //byte、short、char、boolean:都以int型来保存
        byte i = 15;
        int j = 8;
        int k = i + j;

       // int m = 800;

    }

    public int getSum(){
        int m = 10;
        int n = 20;
        int k = m + n;
        return k;
    }

    public void testGetSum(){
        //获取上一个栈桢返回的结果,并保存在操作数栈中
        int i = getSum();
        int j = 10;
    }

    /*
    程序员面试过程中, 常见的i++和++i 的区别,放到字节码篇章时再介绍。

     */
    public void add(){
        //第1类问题:
        int i1 = 10;
        i1++;

        int i2 = 10;
        ++i2;

        //第2类问题:
        int i3 = 10;
        int i4 = i3++;

        int i5 = 10;
        int i6 = ++i5;

        //第3类问题:
        int i7 = 10;
        i7 = i7++;

        int i8 = 10;
        i8 = ++i8;

        //第4类问题:
        int i9 = 10;
        int i10 = i9++ + ++i9;
    }
}

栈顶缓存(Top-of-Stack Cashing)技术

动态链接(Dynamic Linking)(或指向运行时常量池的方法引用)

 

代码解释:

public class DynamicLinkingTest {

    int num = 10;

    public void methodA(){
        System.out.println("methodA()....");
    }

    public void methodB(){
        System.out.println("methodB()....");

        methodA();

        num++;
    }

}

#7等就是符号引用

方法的调用

代码解释:

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

方法调用:虚方法与非虚方法

子类对象的多态性的使用前提:1.类的继承关系  2.方法的重写

非虚方法不可以重写

代码解释:

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

关于invokedynamic指令

代码解释:

package com.atguigu.java2;

/**
 * 体会invokedynamic指令
 * @author shkstart
 * @create 2020 下午 3:09
 */
@FunctionalInterface
interface Func {
    public boolean func(String str);
}

public class Lambda {
    public void lambda(Func func) {
        return;
    }

    public static void main(String[] args) {
        Lambda lambda = new Lambda();

        Func func = s -> {
            return true;
        };

        lambda.lambda(func);

        lambda.lambda(s -> {
            return true;
        });
    }
}

方法调用:方法重写的本质

方法调用:虚方法表

代码解释:

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 {
}

方法返回地址(return address)

代码解释:

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 class ReturnAddressTest {
    
    public void method2() {

        methodVoid();
      
        try {
            method1();//72行
        } catch (IOException e) {
            e.printStackTrace();
        }//75行
    }

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


}

一些附加信息

栈的相关面试题

代码解释:

/**
 * 面试题:
 * 方法中定义的局部变量是否线程安全?具体情况具体分析
 *
 *   何为线程安全?
 *      如果只有一个线程才可以操作此数据,则必是线程安全的。
 *      如果有多个线程操作此数据,则此数据是共享数据。如果不考虑同步机制的话,会存在线程安全问题。
 * @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);

    }

}

5-本地方法栈

更多笔记参考网址:https://juejin.im/entry/5e71c5fbe51d4526e651de0b

逃逸分析补充

在这里插入图片描述

在这里插入图片描述

为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器虚拟机、本地方法方法、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全,四种引用类型的使用和别等等。第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值