Java Virtual Machine相关内存结构之线程私有区域

线程私有区域

一、程序计数器

作用:记录下一条JVM指令的地址
特点:线程所属,与虚拟机栈同归线程所有

二、栈和栈帧

— 每个线程运行时所需要的内存空间

栈帧 — 每个方法运行时所需要的内存(方法所需要的参数、局部变量和返回地址都需要分配内存空间)

栈帧和栈之间的联系

  • 当一个方法被调用时,JVM将给该方法划分一段栈帧,并将该栈帧压入栈中;
  • 当该方法执行完,JVM将其所对应的帧弹出栈,亦即释放该方法所占用的内存空间;
  • 栈内可能有多个栈帧:类似递归,当前方法调用另一方法时,JVM将划分一段栈帧给被调用方法并压入栈,以此类推直至对应方法结束、对应栈帧依次出栈,释放。

栈全称为Java Virtual Machine Stacks(Java虚拟机栈)

  • 每个线程运行时所需要的内存成为虚拟机栈
  • 每个栈由一个或多个栈帧(Frame)所组成,对应着每次方法调用时所占用的内存
  • 每个栈只能有一个活动栈帧,对应着当前正在执行的方法

通过一段简单的代码对上述概念进行详解:

public class FramesInStack {
    public static void main(String[] args) {
        method1();
    }

    private static void method1(){
        int arg=13;
        method2(arg);
    }

    private static int method2(int arg){
        int res=arg;
        return res;
    }
}

debug运行上述程序,可以看到将按顺序出现以下结果:

  1. 栈中含有一个栈帧,与main()所对应:帧中参数为主函数相应的参数args数组,打头的P代表Parameters,意为参数

  2. 由于main()调用了method1(),压入method1()对应的栈帧:由于此前方法无需参数而有变量,因此帧中存储的便是该变量,打头的01则代表变量在方法中所定义的顺序

  3. method1()调用了method2(),继续将method2()对应帧压栈:method2则既需要参数且有变量存在,于是帧存储两个变量

  4. method2()执行完毕,弹出栈列(JVM释放该方法内存),栈中仍有两个帧,其余不赘述

  5. method1()执行完毕,弹栈/释放内存,

最后随着main方法结束,栈空

相关问题:

  1. 垃圾回收是否会设计栈内存?

栈帧在每次方法调用结束后都将会弹栈,所涉及内存也将自动被回收;而垃圾回收只是回收堆内存中的无用对象;

  1. 栈内存越大越好吗?

栈内存越大,反而会让内存(计算机/虚拟机的内存使固定的)中所能运行的线程数变得越少;
理论上,越大的栈内存只是让递归调用次数更多而不会增加效率,因此一般不适用-Xss指定栈内存大小,使用默认大小即可;

  1. 多个线程对方法进行调用时,是否会出现局部变量的线程安全问题?

初步结论:每个变量都单独保存线程所对应的帧中,线程之间相对独立,因此不会引发该问题;
进一步的:

  • 实际上如果方法内部的变量仅限于方法的作用范围,那么它将是线程安全的;
  • 而假如局部变量引用了对象的同时,脱离了方法的控制范围,如作为参数传入或返回(按笔者的理解,当作为参数传入时脱离了“在方法体内定义的特性”,而作为参数返回则不满足“当方法执行完成后变量被销毁的特性”;严格意义上讲此时并不算局部变量,在方法之外依旧有可能被其他线程所多次并发调用,若该变量类型非线程安全的变量则不安全,如HashMap和ConcurrentHashMap),都有可能导致该变量与其他线程共享,此时则需要考虑线程安全问题;
  1. 栈内存溢出(java.lang.StackOverflowError)
  1. 栈帧过多导致栈内存溢出:方法递归调用过多,每次递归调用都将会产生一个栈帧,最终栈内存不足而导致栈内存溢出;
  2. 栈帧过大,导致一个栈帧的内存大于栈的内存,此刻栈的内存不足以放下一个栈帧而导致栈内存溢出;

三、本地方法栈(Native Method Stack:本地方法意指由C/C++所编写的方法,Java虚拟机通过这些方法与操作系统底层交互)

Object中的一些方法便是声明为native的

protected native Object clone() throws CloneNotSupportedExpection;
protected native int hashCode();
protected final native void wait();
protected final native void notify();
etc.

注意到,wait()和notify()方法均是定义在Object类中,此处则牵扯到多线程编程的相关问题,即wait()方法与sleep()方法之间的区别;

以上便是JVM中内存结构中的线程私有区域相关总结,文笔水平有限,本随笔的目的主要是对自己复习的总结,当然如果能够帮助到和我一样正在复习或对此知识点有疑问的人那是再好不过了~~~

posted @   喝芬达开高达  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
点击右上角即可分享
微信分享提示