Java虚拟机的堆、栈、堆栈 如何理解

前言

开发过程中,每次用到静态成员,或者静态方法,就会很仔细,生怕用不好造成bug,归其原因就是对jvm了解甚少,那些在堆中存储,是线程间共享的,那些在栈中存储属于线程私有的。借用此文章进行回顾,堆栈作用

堆是堆(heap),栈是栈(stack),堆栈是栈。“堆栈”这种叫法,容易让新人掉坑里

JVM规范让每个Java线程拥有自己的独立的JVM栈,也就是Java方法的调用栈。

Chapter 2.

 The Structure of the Java Virtual MachineEach Java Virtual Machine thread has a 
 private Java Virtual Machine stack,created at the same time as the thread. 
 A Java Virtual Machine stack stores frames (§2.6). 
 language such as C: it holds local variables and partial results,
  and plays a part in method invocation and return.
 to push and pop frames,frames may be heap allocated. 
 The memory for a Java Virtual Machine stack does not need to be contiguous.

同时JVM规范为了允许native代码可以调用Java代码,以及允许Java代码调用native方法,还规定每个Java线程拥有自己的独立的native方法栈。

Chapter 3.

Java Virtual Machine may use conventional stacks, colloquially called
 "C stacks," to support native methods (methods written in a language 
 other than the Java programming language). Native method stacks may 
 Machine's instruction set in a language such as C. Java Virtual Machine
 rely on conventional stacks need not supply native method stacks. 
 If supplied, native method stacks are typically allocated per 
 thread when each thread is created.

这俩都是JVM规范所规定的概念上的东西,并不是说具体的JVM实现真的要给每个Java线程开两个独立的栈。
Oracle JDK / OpenJDK的HotSpot VM为例,它使用所谓的“mixed stack”——在同一个调用栈里存放Java方法的栈帧与native方法的栈帧,所以每个Java线程其实只有一个调用栈,融合了JVM规范的JVM栈与native方法栈这俩概念。
JVM里的“堆”(heap

特指用于存放Java对象的内存区域。

所以根据这个定义,Java对象全部都在堆上。

Chapter 4.

that is shared among all Java Virtual Machine threads.
The heap is the run-time data area from which memory for all class
instances and arrays is allocated.The heap is created on virtual
machine start-up.Heap storage for objects is reclaimed by an automatic
storage management system (known as a garbage collector); 
objects are never explicitly deallocated. The Java Virtual Machine assumes no
particular type of automatic storage management system, and the storage 
management technique may be chosen according to the implementor's 
system requirements. The heap may be of a fixed size or may be 
expanded as required by the computation and may be contracted if a 
larger heap becomes unnecessary. The memory for the heap does not 
need to be contiguous.

要注意,这个“堆”并不是数据结构意义上的堆(Heap (data structure),一种有序的树),而是动态内存分配意义上的堆——用于管理动态生命周期的内存区域

JVM的堆被同一个JVM实例中的所有Java线程共享。它通常由某种自动内存管理机制所管理,这种机制通常叫做“垃圾回收”(garbage collection,GC)

JVM规范并不强制要求JVM实现采用哪种GC算法。

二 从操作系统角度去理解

linux 中一个进程的虚拟内存分布

这里写图片描述

图中0号地址在最下边,越往上内存地址越大。

32位地址操作系统为例, 一个进程可拥有的虚拟内存地址范围为0-2^32。分为两部分,一部分留给kernel使用(kernel virtual memory),剩下的是进程本身使用,
即图中的process virtual memory。普通Java 程序使用的就是process virtual memory.上图中最顶端的一部分内存叫做user stack. 这就是题目问的 stack. 中间有个 runtime heap 就是题目中的heap.

注意在上图中,stack 是向下生长的; heap是向上生长的。当程序进行函数调用时,
每个函数都在stack上有一个 call frame

比如对于以下程序:

public void foo(){

  //do something...
  println("haha"); // <<<=== 在这儿设置breakpoint 1
}

public void bar(){
  foo();
}

main(){
  bar();
  println("hahaha"); // <<<=== 在这儿设置 breakpoint 2
}

当程序运行到breakponit1时,user stack 里会有三个frame ||

main 函数的 frame-------------------|| 

 bar 函数的 frame-------------------<<<=== %ebp|| 

 foo 函数的 frame------------------- <<<===%esp

其中 espebp 都是寄存器。 esp 指向stack 的顶(因为stack 向下生长,esp会向下走);

ebp 指向当前frame的边界。当程序继续执行到brekapoing 2的时候,stack 大概是这样的:

 |-------------------<<<=== %ebp|| 

 main 函数的 frame------------------- <<<===%esp

也就是说当一个函数执行结束后,它对应的call frame就被销毁了。(其实就是espebp分别移动,但是内存地址中的数据只有在下一次写的时候才被覆盖。

说了这么多,终于该说什么东西放在stack 上什么东西放在heap 上了。最直白的解释:

 public void foo(){
  int i = 0; // <= i 的值存在stack上,foo()的call frame 里。
  Object obj = new Object(); // object 对象本身存在heap 里, foo()的call frame 里存该对象的地址。
}

总结

1.区别

java中堆和栈的区别自然是面试中的常见问题,下面几点就是其具体的区别

2.各司其职

最主要的区别就是栈内存用来存储局部变量和方法调用。
而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。

3.独有还是共享

栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。

4.异常错误

如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError。
而如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。

5.空间大小

栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。如果递归没有及时跳出,很可能发生StackOverFlowError问题。
你可以通过-Xss选项设置栈内存的大小。-Xms选项可以设置堆的开始时的大小,-Xmx选项可以设置堆的最大值。

这就是Java中堆和栈的区别。理解好这个问题的话,可以对你解决开发中的问题,分析堆内存和栈内存使用,甚至性能调优都有帮助。

引用地址

http://droidyue.com/blog/2014/12/07/differences-between-stack-and-heap-in-java/

https://www.zhihu.com/question/29833675/answer/45811216

猜你喜欢

转载自blog.csdn.net/o279642707/article/details/79107075