栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JVM学习笔记(二)方法区和栈

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JVM学习笔记(二)方法区和栈

尚硅谷JVM学习笔记 第四章 方法区 1、不同版本具体实现

标准层面:方法区(Method Area)具体实现层面:

≤1.6 永久代=1.7 永久代仍然存在,但是已经开始提出:去永久代≥1.8元空间(meta Space)

TIP

永久代概念辨析:

从堆空间角度来说

新生代:从标准和实现层面都确定属于堆老年代:从标准和实现层面都确定属于堆永久代

名义上属于堆实现上不属于堆 从方法区角度来说

方法区的具体实现:JDK 版本 ≤ 1.7 时,使用永久代作为方法区。方法区的具体实现:JDK 版本 ≥ 1.8 时,使用元空间作为方法区。

2、元

本身含义:万物初始,一件事情的源头或基本组成部分。

举例:元素、元始天尊、每年1月称为元月、1月1日称为元旦、元认知、元无知、元知识

对比类和对象,类相当于是对象的元信息。

3、元空间存储数据说明

类信息:类中定义的构造器、接口定义静态变量(类变量)常量运行时常量池类中方法的代码 第五章 Java栈 第一节 方法栈

方法栈并不是某一个 JVM 的内存空间,而是我们描述方法被调用过程的一个逻辑概念。

在同一个线程内,method01()调用method02():

method01()先开始,method02()后开始;method02()先结束,method01()后结束。

TIP

『栈』和『堆』这两个字辨析:

1、从英文单词角度来说

栈:stack堆:heap

2、从数据结构角度来说

栈和堆一样:都是先进后出,后进先出的数据结构

3、从 JVM 内存空间结构角度来说

栈:通常指 Java 方法栈,存放方法每一次执行时生成的栈帧。堆:JVM 中存放对象的内存空间。包括新生代、老年代、永久代等组成部分。 第二节 栈帧 1、栈帧存储的数据

方法在本次执行过程中所用到的局部变量、动态链接、方法出口等信息。栈帧中主要保存3 类数据:

本地变量(Local Variables):输入参数和输出参数以及方法内的变量。栈操作(Operand Stack):记录出栈、入栈的操作。栈帧数据(frame Data):包括类文件、方法等等。 2、栈帧的结构

局部变量表:方法执行时的参数、方法体内声明的局部变量操作数栈:存储中间运算结果,是一个临时存储空间帧数据区:保存访问常量池指针,异常处理表 3、栈帧工作机制

当一个方法 A 被调用时就产生了一个栈帧 F1,并被压入到栈中,

A 方法又调用了 B 方法,于是产生栈帧 F2 也被压入栈,

B 方法又调用了 C 方法,于是产生栈帧 F3 也被压入栈,

……

C 方法执行完毕后,弹出 F3 栈帧;

B 方法执行完毕后,弹出 F2 栈帧;

A 方法执行完毕后,弹出 F1栈帧;

……

遵循“先进后出”或者“后进先出”原则。

图示在一个栈中有两个栈帧:

栈帧 2 是最先被调用的方法,先入栈,

然后方法 2 又调用了方法 1,栈帧 1 处于栈顶的位置,

栈帧 2 处于栈底,执行完毕后,依次弹出栈帧 1 和栈帧 2,

线程结束,栈释放。

每执行一个方法都会产生一个栈帧,保存到栈的顶部,顶部栈就是当前方法,该方法执行完毕后会自动将此栈帧出栈。

4、典型案例

请预测下面代码打印的结果:34

int n = 10;
n += (n++) + (++n);
System.out.println(n);

实际执行结果:32

使用 javap 命令查看字节码文件内容:

D:record-video-originalday03code>javap -c Demo03JavaStackExample.class
Compiled from “Demo03JavaStackExample.java”
public class Demo03JavaStackExample{
public Demo03JavaStackExample();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.": ()V
4: return

public static void main(java.lang.String[]);
Code:
0: bipush 10
2: istore_1
3: iload_1
4: iload_1
5: iinc 1, 1
8: iinc 1, 1
11: iload_1
12: iadd
13: iadd
14: istore_1
15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_1
19: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
22: return
}

内存执行过程分析:

第三节 栈溢出异常 1、异常名称

java.lang.StackOverflowError

2、异常产生的原因

下面的例子是一个没有退出机制的递归:

public class StackOverFlowTest {

    public static void main(String[] args) {
        methodInvokeToDie();
    }

    public static void methodInvokeToDie() {
        methodInvokeToDie();
    }

}

抛出的异常信息:

Exception in thread “main” java.lang.StackOverflowError at com.atguigu.jvm.test.StackOverFlowTest.methodInvokeToDie(StackOverFlowTest.java:10) at com.atguigu.jvm.test.StackOverFlowTest.methodInvokeToDie(StackOverFlowTest.java:10) at com.atguigu.jvm.test.StackOverFlowTest.methodInvokeToDie(StackOverFlowTest.java:10) at com.atguigu.jvm.test.StackOverFlowTest.methodInvokeToDie(StackOverFlowTest.java:10) at com.atguigu.jvm.test.StackOverFlowTest.methodInvokeToDie(StackOverFlowTest.java:10)

原因总结:方法每一次调用都会在栈空间中申请一个栈帧,来保存本次方法执行时所需要用到的数据。但是一个没有退出机制的递归调用,会不断申请新的空间,而又不释放空间,这样迟早会把当前线程在栈内存中自己的空间耗尽。

第四节 栈空间的线程私有验证 1、提出问题

某一个线程抛出『栈溢出异常』,会导致其他线程也崩溃吗?从以往的经验中我们判断应该是不会,下面通过代码来实际验证一下。

2、代码
new Thread(()->{
    while(true) {

        try {
            TimeUnit.SECONDS.sleep(2);

            System.out.println(Thread.currentThread().getName() + " working");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}, "thread-01").start();

new Thread(()->{
    while(true) {

        try {
            TimeUnit.SECONDS.sleep(2);

            // 递归调用一个没有退出机制的递归方法
            methodInvokeToDie();

            System.out.println(Thread.currentThread().getName() + " working");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}, "thread-02").start();

new Thread(()->{
    while(true) {

        try {
            TimeUnit.SECONDS.sleep(2);

            System.out.println(Thread.currentThread().getName() + " working");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}, "thread-03").start();
3、结论

02 线程抛异常终止后,01 和 03 线程仍然能够继续正常运行,说明 02 抛异常并没有影响到 01 和 03,说明线程对栈内存空间的使用方式是彼此隔离的。每个线程都是在自己独享的空间内运行,反过来也可以说,这个空间是当前线程私有的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/755941.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号