首页 科技正文

欧博亚洲:Java中栈和堆解说

admin 科技 2020-07-01 44 0

之前对JVM中堆内存和栈内存都是一直半解,今天有空就好好整理一下,用做学习条记。   

包罗Java程序在内,任何程序在运行时都是要开拓内存空间的。JVM运行时在内存中开拓一片内存区域,启动时在自己的内存区域中举行更仔细的划分,由于虚拟机中每一片内存处置的方式都差别,以是要单独举行治理。实际上在JVM有五种内存治理形式:

  1. 寄存器;
  2. 内陆方式区;
  3. 方式区;
  4. 栈内存(stack);
  5. 堆内存(heap);

    今天重点梳理一下栈内存和堆内存。

     在解说之前我们要领会一个计算机生长至今仍然无法解决的一个矛盾,就是内存的存取速率和数据巨细之间的矛盾。当我们对存取速率越快,存储的数据量就越少,反之亦然。栈内存、堆内存实在就是对这种矛盾的一种妥协方式,它们有自己的优点也有自己的瑕玷:

  • 栈内存:存取速率要比堆内存快,仅次于CPU中的寄存器,但栈内存中数据巨细和周期时牢固的。
  • 堆内存:可以动态地分配内存巨细,但存取速率慢。

     那么栈内存、堆内存到底存储那些数据呢?

  • 栈内存中存储都是局部变量,栈中数据生存空间一样平常在当前scopes内(可以简朴明白为{...}括起来的区域)包罗所有的基本类型(int、bool、char、float、double、short、long、byte)和引用类型。
  • 堆内存存储时类的工具,即类的实体,通常new确立的都是在堆中,堆中存放的都是实体(工具),实体用于封装数据,而且是封装多个(实体的多个属性)。

    另外,在举例前我们需要领会一个观点,什么是变量?变量是内存中分配的区域的名称。换句话说就是变量实在分配地址的别称,我们通过这个变量的名字就可以找到一个指向这个变量所引用的数据的内存指针。我们知道了变量的类型,也就知道了这个指针地址后面延续几个字节内存储的数据。

    我们以int[] arr=new int[]{1,2,3}为例,它的内存分配如下:

 

 从上图我们可以看到,“变量”是存在栈内存中,“变量所指向的数据”是存在堆内存中的。

   下面我们举一个更为庞大的类, 来展示每一部分到底是怎么存储的:

 

package class1; class Fruit{ static int x=10; static BigWaterMelon bigWaterMelon_1=new BigWaterMelon(x); int y=20; BigWaterMelon bigWaterMelon_2=new BigWaterMelon(y); public static void main(String[] args){ final Fruit fruit=new Fruit(); int z=30; BigWaterMelon bigWaterMelon_3=new BigWaterMelon(z); new Thread(){ @Override public void run(){ int k=100; setWeight(k); } void setWeight(int waterMelonWeight){ fruit.bigWaterMelon_2.Weight=waterMelonWeight; } }.start(); } } class BigWaterMelon{ public int Weight; public BigWaterMelon(int Weight){ this.Weight=Weight; } }

 

内存图如下:

 

统一种颜色代表变量和工具的引用关系

由于方式区和堆内存的数据都是线程间共享的,以是线程Main Thread,New Thread和Another Thread都可以接见方式区中的静态变量以及接见这个变量所引用的工具的实例变量。

栈内存中每个线程都有自己的虚拟机栈,每一个栈帧之间的数据就是线程独占的了,也就是说线程New Thread中setWeight方式是不能接见线程Main Thread中的局部变量bigWaterMelon_3,然则我们发现setWeight却接见了同为Main Thread局部变量的“fruit”,这是为什么呢?由于“fruit”被声明为final了。

当“fruit”被声明为final后,“fruit”会作为New Thread的组织函数的一个参数传入New Thread,也就是堆内存中Fruit$1工具中的实例变量val$fruit会引用“fruit”引用的工具,从而New Thread可以接见到Main Thread的局部变量“fruit”。

 

此外,栈内存有先进后出(Last in first Out)的特点,而且栈中数据生存空间一样平常在当前scopes内(可以简朴明白为{...}括起来的区域),也就是说当方式执行竣事后,方式内的局部变量在内存中就被消灭了。但堆内存不会自动消灭,它回不停地申请新的堆内存地址来存储新的数据。不再使用地旧数据只会看成“垃圾数据”,在C++中需要你手动消灭,在JVM会自动将这些垃圾数据接纳,也就是传说中地GC。

无论是栈内存照样堆内存,内存空间都是有限的。当堆内存没有可用空间时,好比递归没有跳出,JVM会抛出java.lang.StackOverFlowError;当堆内存没有空间时,好比在while循环中不停建立实例,JVM会抛出java.lang.OutOfMemoryError。

 

那么最后一个问题,为什么变量要放在栈内存中,而变量指向的数据要放在堆内存中呢?

我以为可以这样明白,其一,变量实际上是地址的别称,本质上就是个地址,数据量不大,而实体数据量很大;其二,变量当不在使用时内存空间被接纳,这是也就没有指针指向原来的实体数据,也就是成为了垃圾状态,当JVM再此检查时发现没有指针指向这块空间就会将其收回。

------------------------------------------------------

参考博文:https://www.cnblogs.com/pomodoro/p/11912025.html

参考博文:https://blog.csdn.net/jianghao233/article/details/82777789

 

,

px111.net

欢迎进入平心在线官网(原诚信在线、阳光在线)。平心在线官网www.px111.net开放平心在线会员登录网址、平心在线代理后台网址、平心在线APP下载、平心在线电脑客户端下载、平心在线企业邮局等业务。

版权声明

本文仅代表作者观点,
不代表本站Sunbet的立场。
本文系作者授权发表,未经许可,不得转载。

评论