Java(一)Java中Heap和Stack的区别

版权声明:@GaoShan https://blog.csdn.net/weixin_42969976/article/details/86592616
  1. 堆是栈的一个子集
  2. 栈的存取速度仅次于寄存器,存储效率比堆高,可共享存储数据,但是其中数据的大小和生存期必须提前确定
  3. 堆是运行时可动态分配的数据区,速度上比栈要慢,而且数据不共享,大小生存期可在运行时确定
  4. new关键字,是运行时在堆里面创建对象,每new一次都会创建新对象,因为堆数据不共享

例子:
String str1 = new String(“abc”);
String str2 = “abc”;
str1是在堆里面创建的对象
str2是指向栈里值为"abc"的引用变量,str2的执行首先会创建引用变量str2,再查找栈中有没有"abc",有则将str2指向"abc",没有则在栈里面创建一个"abc"再将"str2指向"abc"。
建立一个对象时从两个地方分配内存,在堆中分配的内存实际建立这个对象,在栈中分配的内存只是一个指向这个堆对象的指针(引用)而已

heap和stack在内存中的区别

栈是一种线性集合,其添加和删除元素的操作在同一端尾部完成,栈按照后进先出的方式进行处理
堆是栈的一个组成元素

堆存储:heapstorage
堆存储分配: heapstorage allocation
堆存储管理: heap storage management
栈编址: stack addressing
栈变换:stack transformation
栈存储器:stack memory
栈单元: stack cel


在JVM中,内存分为两个部分,Stack和Heap,这里,我们从JVM的内存管理原理的角度来认识堆和栈,并通过这些原理人情Java中静态方法和静态属性的问题。
Stack(栈)是JVM的内存指令区
Stack管理很简单,push一定长度字节的数据或者指令,Stack指令压栈相应的字节位移,pop一定字节长度数据或者指令,Stack指针弹栈,Stack的速度很快,管理简单,并且每次操作的数据或者指令字节长度都是已知的。所以Java基本数据类型,Java指令代码,常量都保存在Stack中。
Heap(堆)是JVM的内存数据区
Heap的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在Heap中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指指令,保存在Stack中),在Heap中分配一定的内存保存对象实例和对象的序列化比较类似。而对象在Heap中分配好以后,需要在Stack中保存一个4字节的Heap内存地址,用来定位该对象实例在Heap中的位置,便于找到该对象实例。

由于Stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;
而Heap则是随机分配内存,不定长度,存在内存分配和回收的问题。
因此在JVM中另有一个GC进程,定期扫描Heap,它根据Stack中保存的4字节对象地址扫描Heap,定位Heap中这些对象,进行一些优化,比如合并空闲内存块之类的额,并且假设Heap中没有扫描到空闲的区域,统统refresh(将Stack中丢失了对象地址的无用对象清除)这就是垃圾收集的过程


JVM体系结构

什么是数据,什么是指令,对象的方法和对象的属性分别保存在哪里
1)方法本身是指令的操作码部分,保存在Stack中
2)方法内部变量作为指令的操作数部分,跟在指令的操作码之后,保存在Stack中(实际上是简单类型保存在Stack中,对象类型在Stack中保存地址,在Heap中保存值)
上述的指令操作码和指令操作数构成了完整的Java指令
3)对象实例包括其属性值作为数据,保存在数据区Heap中

非静态的对象属性作为对象实例的i部分保存在Heap中,而对象实例必须通过Stack中保存的地址指针才能访问到,因此能否访问到对象实例以及它的非静态属性值完全取决于能否获得对象实例在Stack中的地址指针


非静态方法和静态方法的区别

非静态方法有一个和静态方法很重大的不同:非静态方法有一个隐含的传入参数,该参数是JVM给它的,和我们怎么写代码无关,这个隐含的参数就是对 象实例在Stack中的地址指针。因此非静态方法(在Stack中的指令代码)总是可以找到自己的专用数据(在Heap 中的对象属性值)。当然非静态方法也必须获得该隐含参数,因此非静态方法在调用前,必须先new一个对象实例,获得Stack中的地址指针,否则JVM将 无法将隐含参数传给非静态方法。
静态方法无此隐含参数,因此也不需要new对象,只要class文件被ClassLoader load进入JVM的Stack,该静态方法即可被调用。当然此时静态方法是存取不到Heap 中的对象属性的。
总结一下该过程:当一个class文件被ClassLoader load进入JVM后,方法指令保存在Stack中,此时Heap 区没有数据。然后程序技术器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问Heap 数据区的;如果是非静态方法,由于隐含参数没有值,会报错。因此在非静态方法执行前,要先new对象,在Heap 中分配数据,并把Stack中的地址指针交给非静态方法,这样程序技术器依次执行指令,而指令代码此时能够访问到Heap 数据区了。

静态属性和动态属性

前面提到对象实例以及动态属性都是保存在Heap 中的,而Heap 必须通过Stack中的地址指针才能够被指令(类的方法)访问到。因此可以推断出:静态属性是保存在Stack中的,而不同于动态属性保存在Heap 中。正因为都是在Stack中,而Stack中指令和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属 性。也正因为静态属性被保存在Stack中,所以具有了全局属性。
在JVM中,静态属性保存在Stack指令内存区,动态属性保存在Heap数据内存区。

猜你喜欢

转载自blog.csdn.net/weixin_42969976/article/details/86592616