java变量存储问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java变量存储问题相关的知识,希望对你有一定的参考价值。

看到说 int a=3;int b=3;
1)a和b存储着相同的地址,该地址指向“3”在栈中的地址。Java是通过找到变量a b在栈中的空间中存的地址来找到3的,那么Java是如何找到a和b的呢?
2)"3"在栈中存储之后,这个地址中存储的还会在改变吗?也就是说再令a=4;此时这个“3“和”4“的存储地址是不是不同了?
3)如果a b存的是地址,那么可不可以理解,在方法调用的时候传参传的全部都是地址值(包括基本类型变量),但是基本类型变量会将变量的值也传到栈中,而对象参数值传递对象的地址?这样理解对吗?
3)中 传递基本类型的参数的值在栈中的位置 是不是也在该方法的栈帧中,知道该方法执行完毕才释放?

我对你看到的这个东西很不认同啊,Java里对int类型是传值i的,你定义了两个不同的int类型变量a和b那么它们就是毫无关系的,应该是不同的两个地址才对。

凭我对编译原理的理解,我觉得Java和C什么的一样,对局部变量是这样处理的:
程序在编译的时候就会给变量分配其在栈中的位置。举个例子:
int a = 3; int b = 3;
int c = a + 1;
假设编译时给a分配的地址是“栈底+8”,b的地址是“栈底+12”,c的地址是“栈底+16”,那么这一段代码编译后变成:
mov 8(%ebp), 3
mov 12(%ebp), 3
add 16(%ebp), 8(%ebp), 1
也就是说编译之后,程序里就没有a、b这些东西了,直接通过地址来进行运算。

如果我上面的理解没有问题,那么对你的问题的回答是:
1)不用找。
2)不会改变,这个地址在编译时就分配好了。
3)我认为函数传参的时候对基本类型还是传值的,即会把基本类型的参数的值复制一份放到被调用函数的栈中;对于类变量才是直接扔进去一个地址。不过我不是很确定。
后补充的那一问,没错我同意你的看法。
参考技术A 首先,

1)在java里定义变量int a=3;int b=3;
在内存里会有四个内存块(不是说两个变量的初始值相同就是同一个地址)。
第一、二个内存块,堆内存,存放的是数据,a指向的3和b指向的3。
第三、四个内存块,栈内存,存放了a指向堆内存的3的地址和b指向堆内存的3的地址。
java怎么找到a、b:以我的理解应该是在编译阶段已经把所有的变量定义罗列好,每个变量名在栈内存的地址由解释器实时分配并关联。

2)3数据本身改变,那么只改变堆内存地址之上存放的数据,地址不会发生改变,也就是说,再令a=4,栈内存不变,堆内存对应的地址存放的值变为4。

3)传参都是复制的变量,不是传地址值。比如你把a变量传参给j变量,那么系统做的工作就是在栈内存新建一个j变量地址,在堆新建j内存块,并将a变量的值复制到此内存块。

4)对于你新补充的,当定义了一个形参的时候你要把形参当成是该方法的局部变量。不管方法是不是已经执行完毕,只要再没有对该形参的引用行为,那么该变量就是变成垃圾,等待回收。

还有什么疑问?

望采纳~追问

那个我理解成员变量一定是存在堆当中的,那么当基本类型的成员变量作为参数传给方法的时候,它是如何在栈中存储的呢?
比如
public int Para(int a)

a=4;

在这个方法中 假设传的是a=3;
这个方法在栈中是怎么工作的啊?

追答

当调用这个方法的时候,方法新建一个局部变量a,此时依然是两个内存块,栈内存存放a变量指向的数据地址。堆内存存放a对应的数据本身。假如传入一个a=3,那么栈内存不变,堆内存的3变为4,其内存块也是不变,变的只是该内存块存放的内容。
你只要记住,只要是变量的定义,必然是分栈内存和堆内存,栈内存放地址,堆内存放数据,赋值只能改变堆内存上的数据,不能改变其它。

追问

最后再问一下,main()当中定义的基本变量存在哪里呢?
我看到有说栈数据共享,指的是什么呢?

追答

很抱歉,对于你的问题我去查了一下书,有必要纠正一下自己的答案。

main()本身是一个方法,当中定义的变量依然是局部变量。方法体内的定义的基本数据类型直接存放在栈中,并随着方法体的结束而销毁。方法体内定义的引用数据类型则是在栈中存放地址,在堆中存放数据。

对于你的问题1,因为是基本数据类型的变量,所以是直接存放在栈内存里的,并且当a和b数据相同时,他们指向常量池里的同一个地址。当值发生改变时,那么指向的地址会发现改变,栈内存会创建一个新的内存存放已经改变后的值,如果这个值在常量池里已经存在,那么就不会新建,而是把地址又指向该相同的值。可以听懂吗?

追问

谢谢你这么认真

本回答被提问者采纳
参考技术B 1)你好,其实你可以这么理解,a和b其实就是一个地址的代词,它们是指向栈的,JVM可以访问这个地址对应的空间,这个空间里面又可以存放值和地址。
2)3和4是同一个空间。4覆盖了3.
3)java分值传递和地址传递(也就是引用类型)
参考技术C 3个对象 2个 String 对象 和一个abc的字符串对象~
java中所有的字符串对象都是唯一的它们都储存在字符串池里
参考技术D 首先你能提出这些问题,说明你很用心的在学习!赞一个!
问题一我还真不知道如何作答!
问题二,当a=4时,用到的是同一个栈内存不是3,4里存储地址,当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间, 该内存空间可以立即被另作他用。 所以尽量使用基本类型的变量
问题三,对象参数值分传的是对象值,还是对象的引用!在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象

Java原始类型变量存储

【中文标题】Java原始类型变量存储【英文标题】:Java primitive type variable storage 【发布时间】:2021-08-01 06:13:54 【问题描述】:

我了解对象变量存储引用(内存地址)和原始变量类型存储值,但原始变量是否仍然需要内存地址来定位值?因此,原始类型变量是否实际上将 ref(mem addr)存储到值?

ie int num = 10; //num是10在内存中的位置?

【问题讨论】:

任何变量都必须在内存中有一个位置来存储它的值,是的。 有关更多信息,您可能想了解 java 如何使用堆栈和堆:journaldev.com/4098/java-heap-space-vs-stack-memory 【参考方案1】:
ie int num = 10; //num is the location of the 10 in memory?

实际上,它在堆栈中。

至少,局部变量和参数是。所以,在:

public void foo() 
   int x = 10;
   System.out.println(x);
   int y = 20;
   System.out.println(y);

会发生什么(编译上面的代码,然后运行javap -c 来检查字节码并继续),javac 会将它编译成如下所示的字节码:

STARTMETHOD foo SLOTS=1
LOADC 0, 10 # load constant value '10' into the first slot (slot 0)
PUSH # Push this on the stack.
INVOKESTATIC java/lang/System println(int)
LOADC 0, 20 # load constant value '20' into... the first slot!
PUSH
INVOKESTATIC java/lang/System println(int)

注意:这过于简单化了; System.out.println 实际上是一个GETFIELD,然后是一个接口调用结果,以及更多这样的简化。但是与栈和槽相关的位代表了它是如何工作的。

你在这里看到了一些有趣的东西。值得注意的是,局部变量与内存位置不是一对一的匹配。 Javac 意识到当你创建 y 时你从不使用 x,所以它只是重复使用同一个 slot。

那个“插槽”的东西被声明在堆栈上。堆栈是内存,但它不断被重用。堆栈是一小块内存(想想 1MB 左右),每次调用一个方法时,你来自哪里,它拥有的所有状态(本地变量等)都存在那里,所有新状态(新方法的本地变量,例如这些插槽)被放在“顶部”。然后当你从一个方法返回时,那个“指向栈顶的指针”正好回到它原来的位置,所以你调用的下一个方法会立即覆盖这些东西。

那么,num 是“内存地址”吗?不是真的,因为地址实际上是“0”(第一个插槽)。这不会转化为“我们堆栈上的第 0 个项目”。它转换为“进入此方法时的堆栈顶部”,将其称为“内存位置”有点牵强。

在任何情况下,这个变量都不存在于堆上根本 - 堆栈与堆是分开的。

对于字段,情况有所不同。给定:

class Foo 
   int num = 10;
   Object o = new Object();

这是完全不同的。 Foo 的实例由一堆堆上的内存表示。具体来说,一些字节来注册这是一个 Foo 实例,然后是一些字节来存储“10”(字面意思是,0x00 00 00 10 显示在内存中,可能是 0x10 00 00 00,具体取决于事物的字节序 - 如果你 coredump 进程内存空间),然后是一些字节来存储一个 'ref' 到那个对象 o。

对该字段的引用永远不会存储为内存地址。它存储为此 Foo 实例的“引用”,VM 将“获取num 字段的值”翻译为“相对于实例所在位置的第 10 个偏移量”。

如果您想将其称为内存地址,请成为我的客人。但是这样规定有什么意义呢?

'ref' 就像一个指针,除了它通常不是直接的内存地址,而是 JVM 可以用来确定内存地址的某个数字。通常对象在 8 字节边界上对齐,因此在某些 VM 上,这实际上存储了目标内存地址的 8 分之一,或者它是相对于某物的内存地址,或者它只是稍后查找的键。这取决于 VM 和 GC 实现,了解这些东西的理由为零,你无法从 java 代码中观察到任何这些。

【讨论】:

当我写下我的问题时,我突然意识到原语必须存储在堆栈上(而对象存储在堆上)。如果原语在堆栈上,那么它是如何工作的? int a = 5;诠释 b = 10; System.out.println(a); b 将位于堆栈的顶部,因此要访问 a,Java 是否将 a 洗牌到堆栈的顶部以访问它? java 是按值传递的。 a 被解析,它解析为的 value 被压入堆栈。副本,有效。没有洗牌 - 复制完成。它与System.out.println(a + b) 没有什么不同。如果你觉得这些东西有趣,就开始运行javap,而不是问 SO 问题(它对你作为 Java 程序员的技能的影响为零,所以如果这是你的目标,那就找点别的东西去痴迷吧。但如果你觉得这些东西很有趣,对你有好处!javap -c(即查看字节码)是了解更多信息的下一步。 @chappie – 变量都在堆栈中。基元和对象引用都是一样的,只要它们不是对象的一部分(尽管它们通常被称为 attributesproperties 而不是 variables)。更准确地说,这些变量的值在堆栈上,这些值是……原语或引用! 还有一个问题。每个对象(在堆上)的内存是否像数组一样连续?即汽车类有品牌、型号、价格。品牌、型号和价格是连续内存吗? 是的,单个对象的所有字段数据都是连续的,但是请记住,存储来表示对象的东西只是一个指针。他们指向的实际对象不必是连续的。【参考方案2】:

退后一步……

对象变量存储对对象(的“地址”)的引用。如您所料,这样的对象地址是一个(数字)值。

如果原语的变量是该值的地址,那么对象和原语之间会有什么区别(以及为什么 Java 需要 Integer 类,而它已经有了 ìnt?)

所以,不,原始变量直接保存原始变量的值。

当然,这个值有一个地址,只是这个地址不能被 Java 代码访问。

【讨论】:

"...对象和原语有什么区别" 这就是为什么我问这个问题 b/c 想到了同样的问题。

以上是关于java变量存储问题的主要内容,如果未能解决你的问题,请参考以下文章

java变量存储

java 8中存储的静态变量在哪里? [重复]

Java最终局部变量存储在哪里?

会话变量存储在 java web 应用程序中的位置

在java中存储状态变量的最佳实践是啥? [关闭]

java - 如何使用java将sql结果集字段存储到单独的数组变量中?