本章节笔记内容与截图均学习和摘抄于:https://www.cnblogs.com/czwbig/p/11127124.html
博主:czwbig
写得非常棒的文章,强烈推荐阅读原文,限于自己的水平,本章节仅摘抄记录一些知识点,方便自己了解和随时查阅。
改动了一些段落方便自己理解,自己做的笔记不一定对,如果需要干货请一定阅读原文!!!
线程私有,是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
计数器记录线程字节码的行号,以便切换线程后能恢复到正常的位置。
如果线程正在执行的是方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。
如果正在执行的是 Native 方法(本地,即当前运行系统自带的非Java方法),这个计数器值则为空
此区域是唯一在JVM规范中规定任何 OutOfMemoryError (内存不足)情况的区域。
为Java方法服务
线程私有,描述Java方法的内存模型。
方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
方法从调用直至执行完成的过程对应栈帧在虚拟机栈中入栈到出栈的过程。
在活动线程中,只有位千栈顶的帧才是有效的,称为当前栈帧。正在执行的方法称为当前方法,栈帧是方法运行的基本结构。在执行引擎运行时,所有指令都只能针对当前栈帧进行操作。
虚拟机栈规定了两种异常状况:
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError(栈溢出)(递归死循环)
如果虚拟机栈扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError(内存不足) 异常。
局部变量表是存放方法参数和局部变量的区域。
如果是非静态方法,则在 index[0] 位置上存储的是方法所属对象的实例引用
操作栈是个初始状态为空的桶式结构栈。
在方法执行过程中, 会有各种指令往栈中写入和提取信息。
也就是说程序对变量进行操作都是从变量表中取数据压入操作栈中进行操作,结束后再更新变量表。
i++ 和 ++i 的区别:
i++:
从局部变量表取出 i 并压入操作栈,然后对局部变量表中的 i +1,将操作栈栈顶值取出使用,这样线程从操作栈读到的是+1之前的值。
++i:
先对局部变量表的 i +1,然后取出并压入操作栈,再将操作栈栈顶值取出使用,线程从操作栈读到的是+1之后的值。
每个栈帧中包含一个在常量池中对当前方法的引用, 目的是支持方法调用过程的动态连接。
方法执行时有两种退出情况:
正常退出,即正常执行到任何方法的返回字节码指令,如 RETURN、IRETURN、ARETURN 等;
异常退出。
无论何种退出情况,都将返回至方法当前被调用的位置。方法退出的过程相当于弹出当前栈帧,退出可能有三种方式:
返回值压入上层调用栈帧。
异常信息抛给能够处理的栈帧。
PC计数器指向方法调用后的下一条指令。
为Native 方法(本地,即当前运行系统自带的非Java方法)服务。
线程调用本地方法时候,将不会受到JVM约束。
本地方法可以通过 JNI来访问虚拟机运行时的数据区,甚至可以调用寄存器。典型的如System.currentTimeMillis()方法,获取当前系统的毫秒。
具有和 JVM 相同的能力和权限。
这里的堆特指Java堆,即虚拟机的堆。注意和系统实际内存的堆区分。
堆是 Java 虚拟机所管理的内存中最大的一块。
堆是被所有线程共享的内存区域。
在虚拟机启动时创建。
堆的唯一目的就是存放实例化的对象,几乎所有的对象实例都在这里分配内存。
堆是垃圾收集器管理的主要区域,因此很多时候被称为GC堆。(垃圾堆)
堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
和JVM栈一样如果扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError(内存不足) 异常。
与堆一样,是线程共享区域。
用来存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
虽然在JVM规范中把方法区描述为堆的逻辑部分,但应该与堆区分开来。
和堆一样可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
垃圾收集器较少在此区域出现,其回收目标主要是类的卸载和常量池的回收。
和JVM栈与堆一样如果无法满足内存需求时,就会抛出 OutOfMemoryError(内存不足) 异常。
运行时常量池是方法区的一部分。
Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池。
常量池用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池存放。
直接内存并不是虚拟机运行时数据区的一部分,也不受JVM约束。
JDK1.4的新特性使得Java使用系统本地方法即Native方法时直接分配给系统内存。