简单谈谈Java中的栈和堆
作者:巴黎爱工作 发布时间:2022-07-30 05:33:01
人们常说堆栈堆栈,堆和栈是内存中两处不一样的地方,什么样的数据存在栈,又是什么样的数据存在堆中?
这里浅谈Java中的栈和堆
首先,将结论写在前面,后面再用例子加以验证。
Java的栈中存储以下类型数据,栈对应的英文单词是Stack
基本类型
引用类型变量
方法
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。
Java的堆中存储以下类型数据,堆对应的英文单词是Heap
实例对象
在函数中定义的一些基本类型的变量(8种)和对象的引用变量都是在函数的栈Stack内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
堆Heap内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。
引用变量是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。而数组&对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在地代码块之外,数组和对象本身占用的堆内存也不会被释放,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!
class Person {
int age;
}
public class LearnHeap {
public static void main(String args[]){
int a=10;
Person person = new Person();
person.age =20;
change(a,person);
System.out.println("a="+ a+",and person.age = "+person.age);
}
static void change(int a1, Person person){
a1 = 11;
person.age= 21;
System.out.println("a1="+ a1+",and age1 = "+person);
}
}
两次的输出结果是什么?猜测下。
以上程序内存加载的执行步骤:
第1步 —— main()函数是程序入口,JVM先执行,首先将main方法压入栈中,在栈内存中开辟一个空间,存放int类型变量a,同时附值10。
在堆中分配一片区域,用来存放和创建Person对象,这片内存区域会有属于自己的内存地址,假设是1001,然后给成员变量赋值,age=20
执行结束后,构造防范弾栈,Person创建完成,将Person的内存地址1001赋值给person(此处person小写,是引用变量类型)
第2步 —— JVM执行change()函数,在栈内存中又开辟一个新的空间,存放int类型变量a和对象Person中person
此时main空间与change空间并存,同时运行,互不影响。
第3步 —— change()方法执行,将a赋值为11,person对象的堆中年龄age赋值为21
第4步 —— change()执行完毕,变量a立即释放,空间消失。但是main()函数空间仍存在,main中的变量a仍然存在,不受影响。而person在堆中对应的地址,所指的age已经赋值=21
实际输出如下:
结论:
如果a()方法中的基本类型(8个)变量x传入b()方法中,并在b()中进行了修改,则a()方法中的x的值保持不变
如果a()方法中的引用类型 变量x传入b()方法中,并在b()中进行了修改,则a()方法中的x的值与b()保持一致
下面对8中基本类型进行简单的测试
package heapandStack;
public class LearnHeap02 {
public static void main(String args[]){
byte b=10;
short s=20;
int i=30;
long l =40l;
float f =12.34f;
double d = 100.123d;
char c = 'A';
boolean flag = true;
change(b,s,i,l,f,d,c,flag);
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(c);
System.out.println(flag);
}
static void change(byte a, short b, int c,long d, float f, double g, char h,boolean x){
a =11;
b=21;
c =31;
d =41l;
f=12.99f;
g= 200.123d;
h ='V';
x =false;
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(f);
System.out.println(g);
System.out.println(h);
System.out.println(x);
}
}
下面测试一下数组,是不是属于实例对象类型
public class LearnHeap03 {
public static void main(String args[]){
int a[] ={1,2,3};
change(a);
for(int i:a)
System.out.print(i+" ");
}
static void change(int[] a){
a[0]=4;
a[1]=5;
for(int i:a)
System.out.print(i+" ");
System.out.println("============ ");
}
}
从结果看出,数组的值被改变了
Java种8种基本数据类型,并不包含String,String的值会被change函数改变吗?String应该存在栈中,还是堆中呢?
先直接上测试代码
public class Learn04 {
public static void main(String args[]){
String s1 = new String("abcd");
String s2 = "asdfghjkl";
System.out.println(s1+", "+s2);
change(s1,s2);
System.out.println(s1+", "+s2);
}
static void change(String s1,String s2){
s1 ="123456";
s2 ="000000";
System.out.println(s1+", "+s2);
}
}
两种的形式来创建String,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。 而第二种是先在栈中创建一个对String类的对象引用变量s2,然后查找栈中有没有存放"asdfghjkl",如果没有,则将"asdfghjkl"存放进栈,并令str指 向”abc”,如果已经有”asdfghjkl” 则直接令s2指向“asdfghjkl”。
既然讲到两种形式创建String,下面讲一个String两种形式创建的区别,先看一段代码
public class Learn05 {
public static void main(String args[]){
String s1 = new String("abcd");
String s2 = "abcd";
boolean a =s1.equals(s2);
boolean b =(s1==s2);
System.out.println(a);
System.out.println(b);
String s3 = "abcd";
boolean a1 =s3.equals(s2);
boolean b1 =(s3==s2);
System.out.println(a1);
System.out.println(b1);
}
}
总结
来源:https://www.cnblogs.com/qianjinyan/p/10352749.html


猜你喜欢
- 1 引言在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchro
- mybatis-plus想要修改某字段为null问题场景使用mybatis + mybatisPlus进行修改某字段,想要将其设为null,
- 这篇文章写的非常好,深入浅出,关键还是一位大三学生自己剖析的心得。这是我喜欢此文的原因。下面请看正文:作为一个大三的预备程序员,我学习and
- 首先在xml文件添加Spinner控件:<Spinner &n
- 一:什么是逆行工程。MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDB
- 方案1.设置一个悬浮的视图挂在recycleView顶部,随着item的移动位置,悬浮标题自动跟随移动或者是保持原地不动。2.使用recyc
- Mybatis所需要的jar包:需要引用两个jar包,一个是mybatis,另一个是MySQL-connector-Java,如果是mave
- 本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflux的全局异常处理机
- 这几天在排查一个堆外内存泄漏的问题时看到很多人都提到了gperftools这个神器,想要尝试一下结果发现它对macOS的支持不太友好。而且大
- 我就废话不多说了,大家还是直接看代码吧~import com.alibaba.fastjson.JSON;import java.util.
- JavaWeb项目部署到服务器详细步骤本地准备在eclipse中将项目打成war文件:鼠标右键要部署到服务器上的项目导出项目数据库文件MyS
- 一、网络爬虫的基本知识网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念。爬虫如何遍历网络呢,互联网可以看做是一张大
- 在 Java 中,方法调用一般通过 Virtual Call 还有 Classic Call。Classic Call 就是直接指向方法的地
- private void btnSetOk_Click(object sender, EventArgs e) &nb
- 本文实例为大家分享了Android实现弹幕效果的具体代码,供大家参考,具体内容如下首先分析一下,他是由三层布局来共同完成的,第一层视频布局,
- 使用GroupingSearch对搜索结果进行分组Package org.apache.lucene.search.grouping Des
- 我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传统方法来解决这个问题,下面我将详
- 群主发普通红包,某群有多名成员,群主给成员发普通红包,普通红包的规则:群主的一笔金额,从群主余额中扣除,平均分成n等份,让成员领取;成员领取
- 本文实例讲述了C#实现鼠标移动到曲线图上显示值的方法。分享给大家供大家参考。具体实现方法如下:一、问题:完成折线图报表后,产品经理要求把折线
- 本文实例讲述了Android编程判断SD卡是否存在及使用容量查询实现方法。分享给大家供大家参考,具体如下:1.判断SD卡是否存在 返回tru