面试:什么是堆,它和栈有什么区别?

demo1

  1. public class Test01 {
  2. public static void main(String[] args) {
  3. int i=10;
  4. test();
  5. }
  6. public static void test(){
  7. int j=100;
  8. }
  9. }

image.png
以上代码的执行顺序:

  1. main方法,java虚拟机开辟了一个栈帧,存放变量int i = 10
  2. 执行到test方法是,java虚拟机栈新开辟一个栈帧,存放变量int j = 100
  3. test方法执行完后,test栈帧被回收(先进后出),但是main栈帧没执行完就没回收

demo2

说明:下面的图画错了,应该是main技术栈

  1. public class Test02 {
  2. public static void main(String[] args) {
  3. int [] x=new int[5];
  4. x[0]=100;
  5. x=null;
  6. }
  7. }

image.png 以上代码的执行顺序:

  1. 执行main方法,java虚拟机栈开辟了一个栈帧

当执行int [] x = new int[5]时,
栈开辟了x变量空间,该变量存放的是堆的引用地址。
堆开辟了x数组空间,例如0x0001,存放数组的值。数组的元素都设置默认值0
栈里的x存放的是堆的引用地址,例如x = 0x0001。

  1. 执行x[0] = 100时,

堆会为x[0] = 100
栈的x变量不会变,继续引用堆的地址。

  1. x = null的时候

image.png
当执行x=null时,x引用的内存地址被删除,例如栈帧删除了0x0001。而堆中的数组不再被x使用后,就会变成垃圾,堆不会立即删除,它是采用垃圾回收算法删除。

demo3

  1. public class Test03 {
  2. public static void main(String[] args) {
  3. int [] x=new int[5];
  4. x[0]=100;
  5. int [] y=x;
  6. x=null;
  7. }
  8. }

image.png

对象和堆的关系

来看demo代码:

  1. public class Test04 {
  2. public static void main(String[] args) {
  3. Book book1=new Book("java","agan");
  4. Book book2=new Book("jvm","agan");
  5. }
  6. public static class Book{
  7. private String name;
  8. private String author;
  9. public Book(String name, String author) {
  10. this.name = name;
  11. this.author = author;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public String getAuthor() {
  20. return author;
  21. }
  22. public void setAuthor(String author) {
  23. this.author = author;
  24. }
  25. }
  26. }

image.png
定义了2个对象,栈开辟了2个空间存放堆的地址,堆开辟了2个空间存储数据。
只要每次new一个对象,堆都会为对象分配空间存储。
再来看下面的代码

  1. public static void main(String[] args) {
  2. Book book1=new Book("java","agan");
  3. Book book2=book1;
  4. }

image.png
book1就不用再讲解了,定义book2时,把book1的地址复制给了book2,故book1和book2引用了相同的堆地址。

总结:

  1. 基本数据类型、局部变量存放在栈帧内,方法执行完毕(栈帧出栈)立即释放,节约空间。
  2. 栈帧内的变量,没有默认值,需要手工设置。
  3. 栈帧只存留在单个线程中,其他线程访问不了。
  4. 可以使用-Xss来定义栈内存大小。
  5. 当堆栈内存已满时,Java运行时抛出java.lang.StackOverFlowError

  1. 数组和对象存于堆中,用完后(栈帧不再引用),靠垃圾回收算法清除。
  2. 堆内存对象的变量都有默认值。
  3. 所有的线程共享堆,即所有栈帧的变量都能引用堆的内存地址。
  4. 可以使用-Xms和-Xmx的JVM选项来定义堆内存的启动大小和最大大小。
  5. 如果堆内存已满,则抛出java.lang.OutOfMemoryError:Java堆空间错误。