Java虚拟机所管理的内存将包括以下几个运行时数据区域:
线程共享区:方法区、堆
线程私有区:虚拟机栈、本地方法栈、程序计数器
※※※※※※※※
举例子: String str = new String(“hello”);
上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而**"hello"这个字面量放在静态区(方法区,非堆)**。
一丶
1.程序计数器:
是一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。
java多线程就是通过对线程的轮流切换并分配处理器的执行时间的方式来实现的,在任意时刻,一个处理器都只会执行某一个线程中的指令。所以每条线程为了切换后能恢复到正确的执行位置,每条线程都会有一个独立的程序计数器,各条线程之间互不影响,独立存储。
2.Java栈:
-
Java栈也称作是虚拟机栈,也就是我们常常所说的栈。事实上,Java栈是Java方法执行的内存模型。
-
存储:
1)运行方法的形参
2)局部变量
3)返回值下图表示了一个Java栈的模型:
局部变量表:用来存方法中的局部变量(包括在方法中申明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储他的值,对于引用类型的变量,存的是指向对象的引用。操作栈帧:程序中的所有计算过程都是在借助于操作数栈来完成的。
指向运行时常量池的引用:因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时的常量池
方法返回地址:当一个方法执行完毕后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法的返回地址。
由于每个线程执行正在执行的方法可能不同,因此每个线程都有一个Java栈,互不干扰。
3.堆区
- 1)new之后的真实数据
- 2)类的非静态成员变量(基本数据类型和String类型的引用),即全局变量
4.方法区(非堆,静态区)
存储:
- 1)静态变量,存储于静态域,
- 2)常量(包括final修饰的常量和String常量),存储于常量池
- 需要特别注意的是:
方法区是线程安全的。由于所有的线程都共享方法区,所以,方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入JVM,那么只允许一个线程去装载它,而其它线程必须等待 !
4_1常量池:
常量池是方法区的一部分内存。常量池在编译期间就将一部分数据存放于该区域,包含基本数据类型如int、long等以final声明的常量值,和String字符串、特别注意的是对于方法运行期间,位于栈中的局部变量String常量的值可以通过 String.intern()
方法将该值置入到常量池中。
4_2静态域:
存放在类中以static声明的静态成员变量
二丶
1.全局变量与局部变量
局部变量:(存在栈中,方法结束自动销毁)
在方法中声明的变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因。
在方法中生明的变量可以是基本类型的变量,也可以是引用类型的变量:
- 当声明是基本类型的变量的时:其变量名及值(变量名及值是两个概念)是放在方法栈中。
- 当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆中的
成员变量:(放在堆中)
在类中声明的变量是成员变量,也叫全局变量
同样在类中声明的变量即可是基本类型的变量,也可是引用类型的变量:
- 当声明的是基本类型的变量,其变量名及其值放在堆类存中。
- 当声明的是引用变量时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。
2.堆、栈和方法区存储数据的区别
- 栈:
为即时调用的方法开辟空间,存储局部变量值(基本数据类型),局部变量引用。当一段代码或者一个方法调用完毕后,栈中为这段代码所提供的基本数据类型或者对象的引用立即被释放;注意:局部变量必须手动初始化。
- 堆:
存放引用类型的对象,即new出来的对象、数组值、类的非静态成员变量值(基本数据类型)、非静态成员变量引用。其中非静态成员变量在实例化时开辟空间初始化值。
- 方法区:
存放class二进制文件。包含类信息、静态变量,常量池(String字符串和final修饰的常量值等),类的版本号等基本信息。静态成员变量是在方法区的静态域里面,而静态成员方法是在方法区的class二进制信息里面(.class文件和方法区里面的二进制信息不一样,读取.class文件按照虚拟机需要的格式存储在方法区,这种格式包括数据结构方面。)因为是共享的区域,所以如果静态成员变量的值或者常量值(String类型的值能够非修改)被修改了直接就会反应到其它类的对象中。
3.不同线程调用方法为什么是线程安全的?
答:任何方法每次被线程调用,都会在栈中开辟新的空间。同一方法的不同线程执行,方法与方法之间互不影响。全局变量因为是存在堆区的对象中,所以会互相干扰。
4.从堆和栈的功能以及作用来比较,堆和栈有什么不同?
答:堆主要用来存放对象,栈主要是用来执行程序的。相较于堆,栈的存取速度更快,但栈的大小和生存周期必须确定,因此缺乏一定的灵活性。而堆却可以在运行时动态的分配内存,生存期不用提前告诉编译器,但这也导致了其存取速度的缓慢。
5.成员变量存储在哪儿?
静态成员变量存储在方法区,非静态成员变量存储在堆区。
6.为什么局部变量不能够static修饰?
局部变量存储在栈区,在方法调用时不能够自动初始化,必须由程序员手动初始化,否则会报错,归根结底是由于static变量和局部变量存储的位置不一样。