Java堆和栈的区别/联系详解

java 专栏收录该内容
103 篇文章 8 订阅

Java堆和栈的区别/联系详解

关于Java中堆栈内存的知识,算是基础知识,和C语言中的指针有一些类似,面试中也经常会被问到,特别是跟Java和C都有关的开发工作。

一.堆栈的联系

在Java中,内存分为两种,一种是栈内存,另一种就是堆内存。

栈内存的东西肯定是比堆内存保存跟长久的。

举个简单的例子一个类中:static String aa=”AA”(或String aa=new String(“AA”));

其中aa就是就是保存在栈中的,被创建出来的”AA”就是保存在堆中的,然后被指向到某一个栈地址,如果没有静态static,这里的aa也是保存在堆中的。

二.堆内存

1.什么是堆内存?

堆内存是是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。

2.堆内存的特点是什么?

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

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

3.new对象在堆中如何分配?

分配是动态分配的,回收是由Java虚拟机的自动垃圾回收器来管理

三.栈内存

1.什么是栈内存

栈内存是Java的另一种内存,主要是用来执行程序用的,比如:基本类型的变量和对象的引用变量

2.栈内存的特点

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

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

3.栈内存分配机制

栈内存可以称为一级缓存,由垃圾回收器自动回收

四.栈和堆的区别

JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

1.栈堆差异点

(1)堆内存用来存放由new创建的对象和数组。

(2)栈内存用来存放基础数据或静态变量等

(3)堆是先进先出,后进后出

(4)栈是后进先出,先进后出

2.栈堆相同点

(1)都是属于Java内存的一种

(2)系统都会自动去回收它,但是对于堆内存一般开发人员会断开引用让系统回收它

五.数据共享示例

例子:
int a = 3;
int b = 3;
a=4;

第一步处理:

1.编译器先处理int a = 3;
2.创建变量a的引用
3.在栈中查找是否有3这个值
4.没有找到,将3存放,a指向3

第二步处理:

1.处理b=3
2.创建变量b的引用
3.找到,直接赋值

第三步改变:

接下来
a = 4;
同上方法
a的值改变,a指向4,b的值是不会发生改变的

PS:如果A,B是两个对象的话,那就不一样了,对象指向的是同一个引用,一个发生改变,另一个也会发生改变

比如:
YY y=new YY();
YY a=y;
YY b=y;

如果y改变了,a、b都会改变。
还有这里a==b是正确的。
但是换成: a=new YY(); b=new YY(); ,然后a==b就不正确了,因为这里比较的是一个地址

六.栈堆的其他理解

栈内存更多的说的是一个地址,就像我们经常说的内存中地址的引用,创建的对象被某一个地址持有。
内存对象被持有并不特指栈内,堆也是可以持有堆内的对象。但是能长久只有该对象的只有栈内能做到。
有些是栈内的堆,但是它里面的堆内,还持有堆,里面还有对象,这里面的对象也是被长久持有的。

1.栈对象的建立:

String aa;
int a;
这里只是建立的栈的对象。

2.堆对象的建立:

new String(“1122”);
new AA();
这里只是建立了堆的对象。

3.栈堆的同时建立

AA aa=new AA();//这里AA是一个自定义类名
这里在栈上面建立了aa的位置,在堆中建立了AA类的对象。
这里堆内存的对象被指向到栈内存地址aa。

4.栈堆对象的回收

都是系统负责回收,但是堆的回收肯定比栈回收快!
堆内对象的回收是系统发现该对象不再使用或内存容量不足的情况,才回收。
但是栈对象的回收是在系统即将退出的时候才回收,也就是说:栈对象在创建之后知道程序退出,栈对象一直存在!

5.栈堆对象的回收示例

注意这里对象置空,不等于对象回收,不管是堆内还是栈内!

比如:

//已有自定义AA类

class RR{

    static AA a;

    public void method(){
        AA aa=new AA();
        a=aa;
        aa=null;
    }

}

没调用RR类内的方法method,栈对象a也会存在,但是指向的是一个空值。
调用了method方法后,栈内的a就会指向创建出来的AA类对象。
可以在method方法中,有aa=null;这里只是把aa对象置空,并没有回收new AA()这个对象!因为这个对象已经被栈内的a持有。

就上面简单的几行代码,大家都是可以对栈堆来提出很多设想并论证。。。

比如上面,如果没有把aa赋值給a,那么创建出来的对象new AA()是会被系统回收的(不管有没有a=null;这句话),只是回收的时间由系统决定!

还有一些for循环里面不管创建对象,其实里面的对象也是会被系统不断回收的,但是如果创建过快,回收没那么快,就会造成内存泄漏/溢出。

。。。反正很多假象可以设定

正常情况不会泄漏内存溢出的循环:
  for (int i = 0; i < 1000; i++) {
            TestA a = new TestA();
            System.out.println("打印!" + i + "---" + a);
        }

上面TestA对象没有一直被持有,系统会不断去回收。

会内存溢出的循环:

import java.util.ArrayList;
import java.util.List;

/**
 * 测试内存的类
 */
public class TestB {

    static  List<TestA>list=new ArrayList<>();
    public static void main(String[] arg) {
        System.out.println("打印!");
        for (int i = 0; i < 1000; i++) {
            TestA a = new TestA();
            list.add(a);
            System.out.println("打印!" + i + "---" + a);
        }
    }


}

上面TestA对象创建出来后,一直被集合对象持有,不能回收!
如果后面设置:TestB.list=null;//之前集合对象的对象又出于可以回收状态了。

6.JVM中的堆和栈

JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

从Java的这种分配机制来看,堆栈又可以这样理解:栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。

栈堆内存总结

别人问你,对栈堆的理解,你可以这样说:
(程序所有对象/数据都是保存在内存中,)程序内存分为栈内存和堆内存。
一些基本类型的变量和对象的引用变量都是在栈内存中分配。
堆内存用于存放由new创建的对象和数组。
创建出来的数组和对象在没有引用变量(栈地址)指向它的时候,才变成垃圾,不能再被系统使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针!

本文说了很多栈堆的知识,还有一些东西不知道怎么描述出来,也许会有一些描述得不合理,特别是栈堆和java虚拟机这块理解还不熟。其他的一些知识,或本文有不准确的地方也欢迎大家各抒己见。

共勉:做事不要畏缩,勇往直前,要么君临天下,要么东山再起。

  • 7
    点赞
  • 1
    评论
  • 26
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

峥嵘life

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值