目录
1.程序计数器
1. 程序计数器(Program Counter Register)是一块比较小的内存空间,存储了下一条需要执行的字节码指令的地址
2. 如果当前PC执行的为java方法,则PC记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的为native方法,则PC的值为空(Undefined)
3. Java虚拟机的多线程通过线程轮流切换并分配处理器执行时间的方式实现。在任何确定的时刻,一个处理器都只会执行一条线程中的指令。每个线程的PC记录了当前线程要执行的指令,每个线程都需要一个独立的PC,各线程之间PC互不影响,独立存储,线程私有
4. 当前线程所执行字节码的行号指示器,字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环,跳转,异常处理,线程恢复等基础功能都依赖程序计数器实现
2.java虚拟机栈
1. java虚拟机栈是线程私有的,生命周期与线程相同
2. 栈帧:
虚拟机描述的是java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧(stack Frame)用于存储局部变量表,操作数栈,动态连接,方法出口等信息,每个方法从调用到执行完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
3. 局部变量表:
a. 局部变量表所需的内存空间在编译器完成分配,当进入一个方法时所需要的局部变量空间时完全确定的,在运行期不会改变局部变量表的大小
b. 局部变量表存放了各种基本类型、对象引用和returnAddress类型(指向了一条字节码指令地址)。
c. 局部变量表中的最小单位为slot。jvm规范中没有特定指定slot的大小,通常在32位操作系统中,slot占32位,此时long/double占两个slot。再64位操作系统中,slot占64位。所有数据类型都占1个slot。
d. 参照博客https://blog.csdn.net/weixin_39146499/article/details/106618060进行局部变量表的slot复用等JVM反汇编查看
4. 规定的异常情况有两种:
1.线程请求的栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
2.如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就抛出OutOfMemoryError异常
3.本地方法栈
1. 本地方法栈(Native Method Stacks)与 Java 虚拟机栈所发挥的作用相似,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务
2. Navtive 方法是 Java 通过 JNI 直接调用本地 C/C++ 库,可以认为是 Native 方法相当于C/C++ 暴露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 方法
3. 当线程调用 Java 方法时,虚拟机会创建一个栈帧并压入 Java 虚拟机栈。然而当它调用的是 native 方法时,虚拟机会保持 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简单地动态连接并直接调用指定的 native 方法
4.java堆(初识)
1. java堆是java虚拟机所管理内存中最大的区域,java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,目的是为了存放对象实例。
在java虚拟机规范描述中:所有的对象实例以及数组都要在堆上分配,但是JIT编译器的发展与逃逸分析技术的成熟,栈上分配,标量替换优化技术导致所有对象在堆上分配不是那么绝对了
2. Java堆是垃圾收集器管理的主要区域,也被叫做“GC堆“。由于现在垃圾收集器基本采用分代收集算法,所以还可以分为:”新生代”和”老年代”等。
在java虚拟机规范描述中:Java堆可以是物理上不连续的内存空间,只要逻辑上连续即可
-Xms :表示java虚拟机堆区内存初始内存分配的大小
-Xmx: 表示java虚拟机堆区内存可被分配的最大上限。
通常会将 -Xms 与 -Xmx两个参数的配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新计算堆区的大小而浪费资源。(PS:当初始堆占满后,会尝试进行GC,如果GC之后还不能得到足够的内存,那么就会扩展堆,如果-Xmx设置的太小,扩展堆就会失败,导致OutOfMemoryError错误提示)
5.方法区
1. 方法区是各个线程共享的内存区域
2. 存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。Java虚拟机规范将方法区描述为堆的一个逻辑部分,但是方法区的别名为Non-Heap
3. 方法区中的信息一般需要长期存在,而且它又是堆的逻辑分区,因此用堆的划分方法,我们把方法区称之为老年代。在JDK1.8中已经用元数据存储空间代替。
4. 内存回收效率低:方法区中的信息一般需要长期存在,回收一遍内存之后可能只有少量信息无效.对方法区的内存回收的主要目标是:对常量池的回收和对类型的卸载。和堆一样,允许固定大小,也允许可扩展的大小,还允许垃圾回收。
6.运行时常量池
1. 运行时常量池是方法区的一部分。Class文件中除了有本类的版本、字段、方法、接口等描述信息外,还有常量池。
2. 常量池用于存放编译器生成的各种字面量和符号引用,这些内容将在类加载后进入方法区的运行时常量池中存放。
3. 并不是只有预置在Class文件中的常量池内容才能进入方法区运行常量池,运行期间也可以将新的常量放入池中,例如String类的intern()方法
此博客对运行时常量池有详细的讲解:https://www.cnblogs.com/xiaotian15/p/6971353.html
7.直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,它直接从操作系统中分配,因此不受Java堆大小的限制,但是会受到本机总内存的大小及处理器寻址空间的限制,因此它也可能导致OutOfMemoryError异常出现。在JDK1.4中新引入了NIO机制,它是一种基于通道与缓冲区的新I/O方式,可以直接从操作系统中分配直接内存,即在堆外分配内存,这样能在一些场景中提高性能,因为避免了在Java堆和Native堆中来回复制数据
说明:
以上内容参照
《深入理解java虚拟机》,针对常量池,局部变量表等,后续学习会继续更新!
参照其他博客地址找不到了,如有冒犯,请联系删除