JVM_1.1_运行时内存区域_Java虚拟机栈

版权声明:本文为博主原创文章,可以转载,但请注明出处。 https://blog.csdn.net/Simba_cheng/article/details/82871776

运行时内存区域这块,如果不将内存各个区域做什么的了解清楚,后面看的会很累。

之前将JVM运行时内存区域的内容,整理在了一篇文章中。

在后续深入、细致的学习中,整理的内容越来越多,一篇的话,会导致篇幅过长。

所以将《JVM运行时内存区域详解》分为以下几个章节:

JVM_1.0_运行时内存区域_堆

JVM_1.1_运行时内存区域_Java虚拟机栈

JVM_1.2_运行时内存区域_方法区

JVM_1.3_运行时内存区域_本地方法栈

JVM_1.4_运行时内存区域_程序计数器

JVM_1.5_运行时内存区域_运行时常量池

这里将《Java虚拟机规范中文版》上传了,点击下面链接,即可下载

Java虚拟机规范SE7中文版下载

目录

Java虚拟机栈

《深入理解Java虚拟机:JVM高级特性与最佳实践》

《Java Virtual Machine Specification Java SE 7 中文版》

《Java Virtual Machine's Internal Architecture》

堆与栈

栈是运行时的单位,而堆是存储的单位

为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗


Java虚拟机栈

《深入理解Java虚拟机:JVM高级特性与最佳实践》

Java虚拟机栈是线程私有的,它的生命周期与线程相同

Java虚拟机栈描述的是Java方法执行的内存模型:

每个方法在执行的同时都会创建一个栈帧,它用于存储局部变量、操作数栈、动态链接、方法出口等信息

每一个方法从调用直到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程

《Java Virtual Machine Specification Java SE 7 中文版》

每一条Java虚拟机都有自己私有的Java虚拟机栈,这个栈与线程同时创建,用于存储栈帧。

Java虚拟机栈的作用,就是用于存储局部变量与一些过程结果的地方

另外,它在方法调用和返回中也扮演了重要的角色。因为除了栈帧的出栈和入栈之外,Java虚拟机栈不会再受其他因素的影响

Java 虚拟机规范允许 Java 虚拟机栈被实现成固定大小的或者是根据计算动态扩展和收缩的。

如果采用固定大小的 Java 虚拟机栈设计,那每一条线程的 Java 虚拟机栈容量应当在线程创建的时候独立地选定。

Java 虚拟机实现应当提供给程序员或者最终用户调节虚拟机栈初始容量的手段,对于可以动态扩展和收缩 Java 虚拟机栈来说,则应当提供调节其最大、最小容量的手段。

举个栗子:

User user = new User();

这里的user就是对象的引用,也可以理解为地址,指引着虚拟机要去哪里找user这个对象。如下图:

(图片来自网络)

由图可知,当我们将一个对象作为方法的参数时,在方法中修改这个对象的值,也会影响到原来的对象,因为我们只是改变了图中内存区域的值,它的指引(地址)还是一样的。

同时可以看出,栈中的内存区域是连续的,有大小限制的,如果超过了就会抛出栈溢出的异常StackOverflowError。

在每个方法执行的时候,都会创建一个个栈帧,用于保存局部变量,操作数栈、动态链表等信息。

每次方法的调用都会对应着一个栈帧,因此可以解释有我们在写递归程序的时候不小心报栈溢出的异常,因为栈时有限的,方法调用太多次导致栈帧存满,所以溢出。

《Java Virtual Machine's Internal Architecture》

https://www.artima.com/insidejvm/ed2/jvm8.html

启动线程的的时候,Java虚拟机会为该线程创建新的Java虚拟机栈。

Java虚拟机栈将线程的状态存储在栈中。

Java虚拟机在Java虚拟机栈中只执行两个操作:入栈和出栈。

当线程调用Java方法时,虚拟机会创建一个新栈帧并将其压入当前线程的Java虚拟机栈中。

然后这个新栈帧就成为了当前栈帧。

当方法执行的时,使用该栈帧来存储参数、局部变量、中间计算和其他数据。

方法可以两种方式(之一)完成;正常完成、异常完成。

无论是正常还是突然,Java虚拟机都会弹出并丢弃当前方法的栈帧;然后,先前方法的栈帧变为当前帧。

堆与栈

在网上找到一篇不错的博文,这里摘录下重要的内容。

建议大家看下原文:

《JVM调优总结(一)-- 一些概念》http://hllvm.group.iteye.com/group/wiki/2858-JVM

栈是运行时的单位,而堆是存储的单位

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据堆解决的是数据存储的问题,即数据怎么放、放在哪儿

在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。

栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。

为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗

第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模块化的思想在软件设计的方方面面都有体现。

第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。

第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。

第四,面向对象就是堆和栈的完美结合。其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是,面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;而对象的行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻辑。不得不承认,面向对象的设计,确实很美。

未完待续,后续还有栈帧等内容...

猜你喜欢

转载自blog.csdn.net/Simba_cheng/article/details/82871776