Java 运行时数据区:堆(Heap),栈(Stack),方法区(Method)的区别(总结)
(" ^_^ " 看总结请直接拉到最下面 " ^_^ ")
简单介绍一下,Java 的整个 JVM 占用的内存可分为两个大区:(如下图)
- 线程共享数据区:线程共享区和JVM同生共死,所有线程均可访问此区域;
- 线程私有数据区:顾名思义每个线程各自占有,与各自线程同生共死;
堆(heap),栈(stack),方法区(method) 是我们日常接触最多的概念,本篇主要介绍区别。
简述:
1. 堆区::
- 数据结构:队列优先,先进先出(FIFO),类似于树结构;
- 内存分配:一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收,分配方式类似于链表(PS:Java 中都是系统 GC,程序员无法进行 GC);
- 缓存机制:使用二级缓存,生命周期与虚拟机的 GC 算法有关(并不是引用为空就立即被GC),调用速度相对较低;
- 存储的全部是对象,每个对象都包含一个与之对应的 class 的信息(class的目的是得到操作指令) ;
- JVM 只有一个堆区(heap),它被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 ;
2. 栈区::
- 数据结构:先进后出(FILO)
- 内存分配:由操作系统自动分配释放,存放函数的参数值,局部变量值等;
- 缓存机制:使用一级缓存,被调用时通常处于存储空间中,调用后被立即释放;
- 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 ;
- 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问;
- 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令);
3. 方法区::
- 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量;
- 方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量;
我们通过一个实例,看一看内存是如何分配的:
public class test { // 运行时, JVM 把 test 类的信息都放入方法区
public static void main(String[] args) { // main 方法本身放入方法区。
Programmer name = new Programmer("Jimmy"); // name 是引用放到栈区里, Programmer 对象放到堆里面
name.print();
}
public class Programmer { // 运行时,JVM 把 Programmer 类的信息都放入方法区
/** 属性名称 */
private String name; // new Programmer实例后,name引用放入栈区里,name对象放入堆里
/** 构造方法 */
public Programmer(String name) {
this .name = name;
}
/** 输出方法 */
public void print() { // print 方法本身放入方法区里。
System.out.println(name);
}
}
}
变量在内存中的分配:
1. 类变量(static修饰的变量,静态变量):在程序加载时系统就为他在队中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期 -- 一直到系统关闭。
2. 实例变量(实例化对象):当你使用java关键字new时,系统在堆中开辟并不一定是连续的空间分配给变量,然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的“物理位置”。实例变量的生命周期——实例变量的引用丢失后,将被GC列入到可回收名单中,但不是马上回收释放;
3. 局部变量:生命在某个方法或者某段代码里面,执行到他的时候在栈中开辟内存,局部变量一旦脱离作用域,内存立即释放。
堆(heap)和栈(stack)的区别:
1. 栈存放的是基本类型变量和对象的引用,当超出作用域后,栈内存会被释放;堆存放的是new出来的对象和数组;
2. 堆可以动态分配内存大小,生存期也不必高速编译器,java的垃圾回收器会自动收走那些不再被使用的对象数据。存放栈中的数据大小与生存期则必须是确定的,缺乏灵活性。
3. 存取速度方面,栈的速度要比堆快,仅次于直接位于CPU中的寄存器。而堆由于要在运行时动态分配内存,所以存取速度较慢;
4. 栈的数据是可以共享的,比如字面量3等,而堆不可以;
5. 栈是一种线性集合,其添加和删除元素的操作应在同一段完成,栈按照“先进后出”的方式进行处理;堆的对象地址是不连续的,可以随时访问;
更多精彩,请关注我的"今日头条号":Java云笔记
随时随地,让你拥有最新,最便捷的掌上云服务