Java程序是怎么运行的
Java程序运行在Java Virtual Machine (JVM)中,JVM提供了Java应用程序在运行时所需要的任何资源的管理器。这就意味着开发者写的应用程序或者创建的应用程序没有能力去直接获取系统资源(不管是硬件还是软件),除非JVM能提供给这些资源。所以在Java中,程序运行顺序如下图:
JVM层使得Java平台能够独立运行,其他编程语言,例如C/C++没有使用类似JVM层的东西,因此它们不是跨平台的语言,即使它们是可移植的语言。它们就像下图一样:
这两种形式有优点也有缺点,Java已经有了自己的生态系统。与此同时,像C/C++这样的编程语言能够直接访问系统资源,从而更有利于优化核心单元的使用,从而产生超级快速和高效的程序。但两者在软件开发领域都有各自的用途。
所有编程语言在编译和执行过程中都有许多相似之处。其中最重要的一点就是内存管理,无论使用哪种语言,内存管理对程序的整体效率都有重要影响,因为管理好内存资源,从而才能管理好应用程序性能。
Java中的运行内存
底层平台提供应用程序所需内存,这是应用程序间常见的一种情况,使得每个应用程序能够以最佳方式运作。Java中,操作系统需要授权,才能使用JVM提供的内存资源。在Java中,JVM内存被划分为5个主要部分,包括方法区、堆、栈、PC寄存器和本地方法栈。
本文主要关注堆和栈。内存不是一张空白纸,程序员不能随意记下数据,必须在使用前进行结构化处理。栈和堆是使用内存时遵循的数据结构,在程序执行期间,存储的数据用于各种目的,这取决于程序的目的是什么。
JVM决定程序执行期间使用的运行时数据区域。一些数据区域是与JVM相关的,这表示它们在JVM启动时被创建,并在整个JVM生命周期中持续存在。但是,每个线程都创建和销毁其他数据区域。JVM可以同时执行多个执行线程,这意味着每个线程都有自己的pc(Program Counter,程序计数器)来维护正在执行的当前指令的位置,还有一个栈帧来保存静态内存分配。
栈
栈是内存中的一种结构,开发人员在其中存储元素,其方式允许只从栈顶检索数据——通常称为先入后出(FILO或LIFO)。每个线程都有一个私有的JVM栈,用于存储与静态内存分配相关的变量。实际上,在我们代码中声明和使用的方法中,特定的原语变量存储在栈区域中。另外,对实际存储在堆内存中的对象的引用也存储在堆栈区域中。因此,任何本地分配的内存都存储在堆栈中。
堆栈内存的默认大小可以使用JVM参数-Xss来更改。有时,如果分配了太多变量或方法递归调用自身,则堆栈可能溢出。所有Java程序员都知道的一个常见错误是Java.lang.stackoverflowerror
,当栈内存不足时提示该错误。Java中的每个方法调用都会在栈中分配一块内存,因此,设计糟糕的递归方法调用很容易占用所有栈内存,导致栈内存溢出错误。
堆
堆是JVM一启动就创建的内存区域,它会一直存在,直到JVM被销毁。与栈不同的是,栈是单个线程的属性(因为每个线程都有自己的栈),堆实际上是由JVM本身管理的全局内存,此内存在运行时用于为对象分配内存。因此,对象的实例化可以是用户定义的类、JDK或其他库类。简而言之,使用new
关键字创建的任何对象都存储在堆内存中。堆内存中的对象可被JVM运行的所有线程访问。访问管理非常复杂,使用了非常复杂的算法,这就是JVM垃圾收集器发挥作用的地方。
堆的默认大小可以使用JVM参数-Xms
和-Xmx
来更改。随着对象的创建和销毁,堆的大小也会增加或减少,如果达到最大内存限制后并尝试进一步分配内存,则抛出java.lang.OutOfMemoryError
。
堆中的字符串池(StringPool)
Java.lang.String
类是Java中使用最多的类,因此,应该特别注意它的效率问题。与基本数据类型相比,字符串的操作效率总是很慢,所以,必须采用某种方式使得字符串对象操作的效率和便利性方面类似或者接近于基本数据类型,为了达到这个目的就在堆中分配了一块特殊内存区域(StringPool),创建的任何字符串对象都由JVM存储在StringPool中。与堆中创建的其他对象相比,这提高了性能。
从代码示例说明堆和栈
为了更好地说明在Java中堆和栈内存的使用,让我们写一个简单的程序,并决定哪个分配分配到哪个内存——堆或栈:
public class HeapAndStackTest { public static void main(String[] args) { int x=10; int y=20; String greet = "Hello"; Date d = new Date(); diff(x, y); } public static int diff(int x1, int x2) { return x2-x1; } }
这段代码运行方式如下:
程序启动,JVM将Java运行时环境(JRE)类加载到堆中。
在遇到
main()
方法时,会创建一个栈帧。局部变量
x
和y
存储在栈中。字符串
greet
分配在堆的StringPool区域中。Date
对象分配在堆区,而它的引用d
存储在栈中。
以上就是Java中的堆和栈是什么的详细内容,更多请关注Work网其它相关文章!