JVM学习笔记

1.运行时数据区

2 运行时数据区及线程概述

JVM将内存划分为俩种类型的数据区域

  • 线程共享:JVM启动时创建,退出时才销毁
  • 线程私有:线程创建时创建,线程退出时销毁

2.1 运行时数据区

JVM内存布局规定了Java运行过程中内存申请,分配,管理的策略,保证高效运行。
不同JVM在内存划分和管理机制方面有部分差异,探讨经典JVM布局HotSpot
划分,线程共享为堆区,方法区,其他私有

  • 本地方法栈
  • 程序计数器
  • 虚拟机栈:以栈帧为基本单位构成,栈帧包括局部变量表,操作数栈,动态连接,方法返回地址,附加信息
  • 堆区:新生代,老年代
  • 方法区

2.2 线程

在HotSpot中,每个线程都与操作系统本地线程直接映射。OS将线程调度到可用的CPU上,本地线程创建成功,调用Java线程中run方法。
当正常执行结束(未出现异常,捕获了异常),Java线程和本地线程都会被回收,并释放相应资源。
出现未捕获异常,Java线程终止,本地线程再决定JVM是否需要终止,当只有守护线程时,JVM自动退出

  • 守护线程:系统性服务,垃圾回收线程,编译线程,手动创建守护线程
  • 非守护线程:用户线程,系统工作线程

3 程序计数器

对物理寄存器的抽象模拟
看作为当前线程执行的字节码的行号指示器,字节码解释器通过改变计数器的值来选取下一条指令。
既没有垃圾回收也没有内存溢出
每个线程都有自己的计数器,生命周期与线程的一致。

  • 程序计数器作用:
    CPU不停切换线程,切换回来以后,需要知道从哪继续执行。
  • 为什么线程私有:
    为准确记录各个线程正在执行的字节码地址,不会互相干扰

4 虚拟机栈

跨平台性,不同平台CPU不同,所以Java指令不能基于内存器,而设计为基于栈
可以跨平台,指令集小,编译器容易实现,缺点是性能较低,实现同样功能需要更多指令。
每个线程都会创建一个虚拟机栈,内部由许多栈帧构成,每个栈帧对应一个Java方法调用。
每一个方法被调用至到执行完毕,对应入栈到出栈过程

栈解决程序运行问题

  • 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器
  • 不存在垃圾回收,存在内存溢出
  • 栈大小可动态扩展的或者固定不变的(HotSpot固定)。如果固定大小,如果线程请求的栈容量超过最大内存,抛异常。
    如果动态扩展,且无法申请足够内存,或者无内存创建对应栈,会抛出OOM异常。使用-Xss设置最大栈空间。
    栈帧是一个内存区块是一个数据集,维系着方法执行过程中的各种数据信息,一个活动线程上,一个时间点上只会有一个活动的栈帧(栈顶),即只有当前正在执行的方法的栈帧是有效的。
    方法正常return或抛异常都会导致栈帧被弹出。

5 局部变量表

定义一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,数据类型包括基本数据类型,对象引用,returnAddress类
基本类型存值,引用类型存指向对象的引用
线程私有数据
容量大小在编译期就确定的。
对一个方法而言,参数和局部变量越多,局部变量表就越膨胀,栈帧就越大。
虚拟机通过变量表完成参数值到参数变量列表的传递过程,方法结束后销毁
最基本存储单元是Slot变量槽,32位以内占用一个,64位(long double)用俩位
JVM为每一个slot分配一个访问索引,通过这个索引可以访问到局部变量表指定的局部变量值
当方法被调用时,方法参数和方法体内部定义的局部变量将按照顺序被复制到局部变量表上的每一个slot
如果一个局部变量过了其作用域,那么在其作用域之后申明新的局部变量有可能会复用过期的变量slot,可以节省资源
局部变量初始化:
静态变量有俩次初始化,一次系统初始化对其赋0值。另一次赋定义的值。
局部变量没有系统初始化过程,必须手动初始化
方法执行时,虚拟机使用局部变量表完成方法传递,变量也是重要垃圾回收根节点,变量表中直接或间接引用的对象都不会被回收

6 操作数栈

栈帧中除了局部变量表还有操作数栈(表达式栈)
用于保存计算过程中中间结果,同时作为计算过程中变量临时存储空间
JVM执行引擎的一个工作区,最大深度在编译期就定义好了,32位一个单位
未采用索引来进行数据访问,而采用入栈出栈完成一次数据访问。
JVM的解释引擎是基于栈的执行引擎,其中栈指的就是操作数栈

7 栈顶缓存技术

当一个栈的栈顶或附近元素被频繁访问,就会将其缓存到CPU的寄存器中,将原本在内存读写操作变成了在寄存器操作,提升执行效率

posted @ 2024-04-17 17:18  lwx_R  阅读(2)  评论(0编辑  收藏  举报