JVM内存的划分(堆栈的理解)

参考文章:

https://www.cnblogs.com/wlwl/p/9463557.html

https://blog.csdn.net/u013159288/article/details/124218944

 

一、JVM内存的划分

在说堆和栈之前,我们先说一下JVM(虚拟机)内存的划分:

      Java程序在运行时都要开辟空间,任何软件在运行时都要在内存中开辟空间,Java虚拟机运行时也是要开辟空间的。JVM运行时在内存中开辟一片内存区域,启动时在自己的内存区域中进行更细致的划分,因为虚拟机中每一片内存处理的方式都不同,所以要单独进行管理。

JVM内存的划分有五片:

  1. 程序计数器(Program Counter Register)
  2. Java虚拟机栈(栈)(Java Virtual Machine Stacks)
  3. 本地方法栈(Native Method Stack)
  4. Java堆(堆)(Java Heap)
  5. 方法区(Methed Area)

jvm五大内存区域(即jvm运行时数据区),描述的是类被加载时,经过解析后,存储到特定的数据区。方法区和堆是所有线程共享的,而栈和计数器是线程私有的。栈处理程序运行的问题,堆处理数据的存储问题。所以才有堆栈分离。

方法区:又被称为元空间,用来存储类的信息,例如:方法,方法名,返回值,常量。当它无法满足内存分配需求时,方法区会抛出OutOfMemoryError。

方法区是用来存储被Java虚拟机加载的类信息,常量,静态变量,运行时常量池等。在jdk8以前,方法区中定义了永久代。因为使用永久代来实现了方法区,所以被描述为堆的一个逻辑部分。但是它确是“非堆”,只是设计堆中的收集器 扩展到了方法区而已。在jdk8的时候,永久代被替换成了元空间(Metaspace)。

在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。

在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

 

堆:Java堆是java虚拟机所管理的内存中最大的一块,是被所有线程都共享的内存区域。
Java中堆是用来存放对象实例(包括成员属性)的。堆是被所有线程共享的,在JVM中只有一个堆。在堆上划分了很多区域,新生代、老年代和永久代。其中新生代又划分为了一个eden区和两个survivor区。Java堆实际上是垃圾收集器(GC)管理的一块内存区域,经过之上的划分得以进行更有效率的垃圾回收。

 

程序计数器:它是一块很小的内存空间,主要用来记录各个线程执行的字节码的地址,例如,分支、循环、线程恢复等都依赖于计数器。

指向当前线程正在执行的行号(地址),用来保证线程切换时回到程序调用的位置。(例如:在a方法里面调用了b方法,代码从上往下执行,执行到调用b方法的那行时,指针会记录下这个位置,然后执行b方法里面的逻辑,b方法正常执行完或异常退出,指针都会回到a方法里面。)

 

虚拟机栈:Java虚拟机栈描述的是线程进栈出栈的过程,线程结束内存自动释放。是线程私有的,每个线程都对应着一个虚拟机栈。它用来存储当前线程运行方法所需要的数据、指令、返回地址(即局部变量和正在调用的方法)。每个方法执行的时候都会创建一个栈帧,它是方法运行时的基础数据结构。栈帧中主要存储的有局部变量表、操作数栈、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。栈帧出栈后,里面的局部变量直接就从内存里清理掉了。

 

本地方法栈:本地方法栈和虚拟机栈类似,其区别是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务(了解即可)。

 

二、小结

方法区:存储 信息,常量,静态变量。
堆:存储 对象实例和成员变量
程序计数器:存储当前线程正在运行的行号(地址)
虚拟机栈:存储当前线程运行方法所需要的数据(局部变量)
本地方法栈:虚拟机使用到的Native方法服务

 

堆内存的特点就是:先进先出
栈内存的特点就是:后进先出

 

posted @ 2022-09-05 21:51  我用python写Bug  阅读(85)  评论(0编辑  收藏  举报