Java中的堆栈

一. 解释

栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。Java自动管理栈和堆,程序员不能直接地设置栈或堆。

1. 栈:

  • 理解:
  • 栈是操作系统在建立某个进程或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
  • 栈内存用来存储局部变量和方法调用(其实这算是实际运行时JVM提供的性能优化)
//如果该局部变量是基本数据类型,那么直接将该值存储在栈中
int a=1;

//如果该局部变量是一个对象,那么将引用存在栈中,而对象{1,2}存储在堆内
int[] array=new int[]{1,2};

java中的基本数据类型一定存储在栈中的吗?

  • 特点:
1). 存取速度比堆要快(不明显),仅次于直接位于CPU中的寄存器。

  2). 栈中的数据可以共享(也就是说,栈中的数据可以被多个变量共同引用)
  • 缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性
  • 异常错误:如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError。

2. 堆:

  • 理解:
  • 每个Java应用都唯一对应一个JVM实例,每一个JVM实力唯一对应一个堆。
  • 应用程序在运行中所创建的所有类实例都放在这个堆中,并有应用所有的线程共享。
  • Java中分配堆内存是自动初始化的,Java中所有对象的存储空间都是在堆中分配的,但这些对象的引用则是在栈中分配的,也就是说,一般建立一个对象是,堆和栈都会分配内存
  • 特点:可以动态的分配内存大小,生存期也不必实现告诉编译器,在堆中分配的内存,有Java虚拟机的垃圾收集器(GC)来管理。
  • 缺点:由于要在运行时动态分配内存,存取速度较慢
  • 异常错误:而如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。

3. 关系

在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量结成了数组或对象的引用变量。

引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。

引用变量就是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。而数组或对象本身在堆中分配,即使程序运行到使用new产生数组或对象的语句之外,他占用的堆内存也不会被释放。

也就是说,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能在被使用,但这时依然占用这内存,之后会在一个不确定的时间呗垃圾回收器释放掉。

二. Java数据存储

速度依次递减

1. 寄存器(register):

这是最快的存储区,因为它位于不同与其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。你不能直接控制,也不能在程序中感觉寄存器存在的任何迹象

2. 栈(stack):

位于通用RAM中,但通过他的“堆栈指针”可以从处理器那里获得支持。
堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。这是一种快速有效的分配存储方式,仅次于寄存器。
创建程序的时候,Java编译器必须知道存储在堆栈内所有数据的确切大小和生命周期,因为它必须生成相应的代码,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些Java数据存储在栈内存中(特别是对象引用),但是Java的对象不存储其中。

3. 堆(heap):

一种通用型内存池,也位于RAM中,用于存放Java对象。
堆不同于栈的好处是,编译器不需要知道要从堆里分配多少存储空间,也不需要知道存储的数据在堆里存活多长时间,因此,在对立分配存储有很大的灵活性。

当你创建一个对象时,会自动在对立进行存储分配。但堆进行存储分配会比用栈进行存储需要更多的时间

4. 静态存储(static storage):

这里的静态是指“在固定的位置”,也是位于RAM,静态存储里存放程序运行时一直存在的数据。可用static关键字来标识一个对象的特定元素时静态的,但对象本身时不会存放在静态存储空间的。

5. 常量存储(constant storage):

常量通常直接置于程序代码内部。因为他们永远都不会改变,有的常量需要严格的保护,所以可以将他们置于只读存储器中。

6. 非RAM存储:

如果数据完全存活于程序之外,那么他可以不受程序的任何控制,甚至在程序没有运行时也可以存在。

三. JVM的内存分区

1. 栈区:

  • 每个线程包含自己的一个栈区,栈中只保存局部变量和方法调用。
  • 每个栈的数据都是私有的,其他栈不可访问。
  • 栈 = 基本类型变量区 + 执行环境上下文 + 操作指令区(存放操作指令)

2. 堆区:

  • 存储的是对象,每个对象都包含一个与之对应的class信息(Clazz.getClass())获取,class的目的是得到操作指令。
  • JVM只有一个堆区,被所有线程共享,堆中只存放对象本身

3. 方法区:

  • 又称静态区,被所有线程共享,
  • 方法去包含所有的class和static变量