堆和栈的区别、Java中成员变量、局部变量、全局变量、静态变量存在位置及初始化

参考文献

堆和栈的区别
堆和栈的概念和区别
什么是堆栈,堆和栈到底是不是一个概念
剑指Offer——简述堆和栈的区别
Java中成员变量、局部变量、全局变量、静态变量存在位置及初始化
因为线程是共享进程的资源的,所以栈是私有的,堆是公有的。
(1)程序内存布局场景下,堆与栈表示两种内存管理方式;
(2)数据结构场景下,堆与栈表示两种常用的数据结构。

Java中堆和栈有什么不同?

为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈 调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己 的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。

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

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

JVM内存的划分有五片:

1、寄存器;
2、本地方法区;
3、方法区;
4、栈内存;
5、堆内存。

堆栈:是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除

栈和堆的特点

1、栈:先进后出。
(1)栈内存就好像一个矿泉水瓶,像里面放入东西,那么先放入的沉入底部,所以它的特点是:先进后出,后进先出

(2)存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性

2、堆:队列优先,先进先出。

(1)堆其实可以类似的看做是管道,或者说是平时去排队买票的的情况差不多,所以堆内存的特点就是:先进先出,后进后出,也就是你先排队,好,你先买票。

(2)堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

栈内存

栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短

存放基本类型的数据和对象的引用,即存放变量
如果是引用类型,则将变量名存入栈,然后指向它new出的对象(存放在堆中);
(线程私有)栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存;
(内存分配)栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。如果递归没有及时跳出,很可能发生StackOverFlowError问题;
(异常提示)如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError;
(内存分配)内存分配固定;
存取速度比堆要快,仅次于寄存器,栈数据可以共享;

堆内存

存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。(它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。)

所有的对象实例及数组都要在堆上分配;
Java堆可以处于物理上不连续的内存空间,只要逻辑上连续即可;

**(线程共享)**堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问;
**(异常提示)**如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError;
**(内存分配)**动态分配内存

堆与栈的不同点:

1、栈内存存储的是局部变量而堆内存存储的是实体(对象);

2、栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;

3、栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

在java中堆一般用来存储对象和数组,栈一般用来存储方法和基本类型(注:基本类型不包括string)变量。

栈数据共享

栈的存取速度比堆快。栈中的数据是可以共享的(解释:比如int a = 3; int b =3; 此时b的指针直接指向3的地址即可,不用重新开辟一块空间存储3的地址)()因为栈内数据共享,int a=3; int b=3; 编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b这个引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。)

堆和栈的相同点:

1、都是属于Java内存的一种
2、系统都会自动去回收它,但是对于堆内存一般开发人员会自动回收它

堆栈

堆栈(英语:stack)又称为栈或堆叠,是计算机科学中一种特殊的串列形式的抽象数据类型,其特殊之处在于只能允许在链表或数组的一端(称为堆栈顶端指针,英语:top)进行加入数据(英语:push)和输出数据(英语:pop)的运算。另外堆栈也可以用一维数组或链表的形式来完成。堆栈的另外一个相对的操作方式称为队列
堆栈数据结构使用两种基本操作:推入(压栈,push)和弹出(弹栈,pop):
推入:将数据放入堆栈的顶端(数组形式或串列形式),堆栈顶端top指针加一。
弹出:将顶端数据数据输出(回传),堆栈顶端数据减一。

堆(英语:Heap)是计算机科学中的一种特别的树状数据结构。若是满足以下特性,即可称为堆:“给定堆中任意节点 P 和 C,若 P 是 C 的父节点,那么 P 的值会小于等于(或大于等于) C 的值”。若父节点的值恒小于等于子节点的值,此堆称为最小堆(英语:min heap);反之,若父节点的值恒大于等于子节点的值,此堆称为最大堆(英语:max heap)。在堆中最顶端的那一个节点,称作根节点(英语:root node),根节点本身没有父节点(英语:parent node)。

示例

例1

下面我们通过一个图例详细讲一下堆和栈:

比如主函数里的语句 int [] arr=new int [3];在内存中是怎么被定义的:

主函数先进栈,在栈中定义一个变量arr,接下来为arr赋值,但是右边不是一个具体值,是一个实体。实体创建在堆里,在堆里首先通过new关键字开辟一个空间,内存在存储数据的时候都是通过地址来体现的,地址是一块连续的二进制,然后给这个实体分配一个内存地址。数组都是有一个索引,数组这个实体在堆内存中产生之后每一个空间都会进行默认的初始化(这是堆内存的特点,未初始化的数据是不能用的,但在堆里是可以用的,因为初始化过了,但是在栈里没有),不同的类型初始化的值不一样。所以堆和栈里就创建了变量和实体

在这里插入图片描述 那么堆和栈是怎么联系起来的呢?

我们刚刚说过给堆分配了一个地址,把堆的地址赋给arr,arr就通过地址指向了数组。所以arr想操纵数组时,就通过地址,而不是直接把实体都赋给它。这种我们不再叫他基本数据类型,而叫引用数据类型。称为arr引用了堆内存当中的实体。(可以理解为c或c++的指针,Java成长自c++和c++很像,优化了c++)
在这里插入图片描述

如果当int [] arr=null;arr不做任何指向,null的作用就是取消引用数据类型的指向

当一个实体,没有引用数据类型指向的时候,它在堆内存中不会被释放,而被当做一个垃圾,在不定时的时间内自动回收,因为Java有一个自动回收机制,(而c++没有,需要程序员手动回收,如果不回收就越堆越多,直到撑满内存溢出,所以Java在内存管理上优于c++)。自动回收机制(程序)自动监测堆里是否有垃圾,如果有,就会自动的做垃圾回收的动作,但是什么时候收不一定。

例2

String是一个特殊的包装类数据。可以用:

   String str = new String("abc");

   String str = "abc";

两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象

第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc”则直接令str指向“abc”。

比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。

   String str1 = "abc";

   String str2 = "abc";

   System.out.println(str1==str2); //true
   

可以看出str1和str2是指向同一个对象的。

  String str1 =new String ("abc");

   String str2 =new String ("abc");

   System.out.println(str1==str2); // false

用new的方式是生成不同的对象。每一次生成一个。

因此用第一种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利于节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String(“abc”);的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

另一方面, 要注意: 我们在使用诸如String str = “abc”;的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的 对象。只有通过new()方法才能保证每次都创建一个新的对象。

由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer(线程安全,效率低)类,以提高程序效率。

Java中成员变量、局部变量、全局变量、静态变量存在位置及初始化

Java中的变量根据不同的标准可以分为两类,以其引用的数据类型的不同来划分可分为“原始数据类型变量和引用数据类型变量”,以其作用范围的不同来区分可分为“局部变量,实例变量和静态变量”。

原始数据类型变量的“变量分配”与“数据分配”是在一起的(都在方法区或栈内存或堆内存)
引用数据类型变量

引用数据类型变量的“变量分配”与“数据分配”不一定是在一起的

根据定义变量位置的不同,可以将变量分为成员变量和局部变量。
成员变量是 定义在一个类的变量:类中方法外
局部变量是定义在一个方法内的变量:方法中

成员变量分为:
静态属性:随类的存在而存在,是在类加载的初始化
非静态属性:随实例的属性存在而存在。

局部变量:
局部变量不能加static,包括protected, private, public这些也不能加。局部变量保存在栈中
局部变量必须在声明时初始化

成员变量和局部变量区别:
成员变量可以不显式初始化,它们可以由系统设定默认值;局部变量没有默认值,所以必须设定初始赋值。
成员变量存在于堆内存,局部变量存在于栈内存

静态变量:类中方法外,用static修饰
生命周期:JVM第一次读到这个类并加载类时开始,类销毁不存在。
类加载过程:

1 加载父类(如果父类已经加载过,则不在加载)。
2初始化静态属性 。
3 按顺序的初始化静态代码块 初始化的前提就是分配空间 。

静态变量存在于方法区即静态区(方法区包含整个程序中唯一存在的元素)
全局变量(在java中可以理解为用static final修饰):在java中全局变量也称成员变量,是创建对象的时候分配内存的。全局常量存于全局数据区
创建对象过程为:

分配空间。
创建父类对象。
初始化成员变量。
调用构造方法创建一个对象。

方法(包括静态方法)中不能有静态变量,方法中存在的是局部变量,而不是静态变量。静态变量是定义在类中,方法体外面的

变量类型存放位置
成员变量
局部变量栈区
静态变量方法区(静态区)
全局变量方法区
全局常量全局数据区
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值