您的位置:首页 > 编程语言 > Java开发

深入理解java内存模型,以及基本数据类型的不同类型的变量的保存方式

2019-05-25 15:45 176 查看

java运行时候的数据区

1.方法区:

准确的说,方法区是针对类的,每个jvm中只有一个方法区,是一个共享的资源,存放类信息(类名称路径名访问修饰符类型(是接口还是类)等等),静态方法,成员方法等,静态变量,成员变量,常量等存放在常量池中,分为两种类型:字面量和符号引用
字面量就是字符串,常量。
符号引用:类和接口的权限定名,字段的名称和描述符,方法的名称和描述符,类名和方法名,在调用方法的时候,能够通过方法名找到方法的引用,并以此定位到函数体,执行函数

这些 字面量和符号引用是在编译期间生成的,这些内容将在类加载之后进入方法区的常量池中存放常量池是class文件中的内容

2.堆区:

每个jvm中只有一个堆区,堆中存放了所有的对象实例(实例中包含对象中的结构)和数组,存放着java的对象,从垃圾回收算法的角度上讲,堆有被划分称为新生代和老年代,新生代被分为8:1:1的比例eden区和fromSurvior、toSurvior
方法区和堆区的内存被线程共享,是线程不安全的

3.栈:

每当创建一个线程的时候,java虚拟机就会为这个线程创建对应的栈,java栈中包含多个栈帧,栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,每个栈帧中存放着局部变量表,操作数栈,方法返回值等相关的信息,每执行一个方法的时候就会创建一个栈帧入栈位于栈顶,栈帧出栈并且清除这个栈帧,位于栈顶的栈帧就是当前正在执行的方法,(局部变量表和方法返回值已经确定了,那么操作数栈在什么时候使用呢?),当在执行一个方法的时候调用了另一个方法,会创建一个新的栈帧放栈定,只有活动的栈帧的局部变量可以被操作数栈使用,当这个栈帧中的所有指令执行完成时候,栈帧将会被移除,刚才栈帧变成了活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数。

局部变量表:

用来存储方法中的局部变量(函数形参和变量(非静态的,方法中不能声明静态的变量,可能是普通变量或者是引用变量,引用了对象))当变量是基本数据类型的时候,直接存放了值,当变量是应用类型的时候,存放的引用的地址(这句话很重要奥)

区分是存放在栈中还是方法区中,就看是(局部变量,创建对象的引用变量)还是(成员变量,静态变量,常量)

操作数栈:

java虚拟机又被称为基于栈的存储引擎,其中的栈指的就是操作数栈,虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。

方法返回值

方法执行完成后的返回地址,每个方法执行完成时候,栈帧就会弹出栈帧中的元素作为方法的返回值

指向运行时常量池的引用

存储程序运行时候可能用到的常量的引用

栈中可能出现两种状况,线程请求的栈的深度大于虚拟机允许的深度将会抛出StackOverflowError异常;如果虚拟机可能动态扩展,如果扩展的时候无法申请到足够的内存,将会抛出OutOfMemoryError异常,可以使用-Xss参数设置栈的大小,栈的大小决定了函数迪奥用的可达深度

4.程序计数器

程序计数器记录着当前正在线程正在执行的字节码行号,每个线程中都有一个程序计数器,因为在多线程中执行是时间片轮转法,要记录一个线程在执行过程中执行到了什么位置,恢复执行的时候依据程序计数器中的数值继续执行下去

5.本地方法栈

本地方法栈和栈是相同的,java栈是为java虚拟机执行java方法提供服务的,而本地方法栈是为jvm执行native方法提供服务的,本地方法栈中也会抛出StackOverFlowError和OutOfMemoryErorr异常。本地方法是非java语言实现的,有些底层的操作或者某些硬件交换信息的时候,用java不方便,不得不适用其他语言编写的一些方法。java虚拟机栈中调用一个方法,这个方法调用本地方法栈,本地方法栈再执行本地方法。

数据如何在内存中进行存储的?

java虚拟机在程序运行时的内存分配有三个位置:堆 栈 方法区

基本数据类型是怎么存储的?

  • 基本数据类型的静态变量
  • 基本上数据类型的成员变量
  • 基本数据类型的局部变量

**

1.基本数据类型的局部变量

**
局部变量是在方法中声明的,存在栈中,栈中的局部变量表中存放了函数形参和成员变量(栈中存放了他们的值或者地址),如果是基本数据类型直接存放着值,如果是引用数据类型方法的是地址

int age=50;
int weight=50;
int grade=6;

在方法区中声明的三个变量直接存在了栈中(忽略其他变量)
int age=50;实际上是分为两步的,首先定义变量 int age; 在java虚拟机中定义了一个名为int age的变量(是变量,如果是常量就是方法区中的运行时常量池中的内容)存放在局部变量表中,然后会在栈中查找是否有字面量(方法区也有字面量:字符串,常量)为50的内容,如果找到了就直接将age指向这个地址,如果没有jvm会在 栈中开辟一个50的内存,并把这个age指向这个内存。

总结:基本类型的局部变量变量和变量值都存在栈中
区分是存放在栈中还是方法区中,就看是(局部变量)还是(成员变量,静态变量,常量)

继续向下执行
int weight=50;
这个时候50的字面量已经在栈中了,直接将局部变量表中的指针指向这个值就行。可见栈中是共享的

继续执行:
weight=40;
这个时候首先会在栈中查找是否存在字面量40,如果不存在的话,就开辟一个空间存放40,如果存在的话就直接指向40,可知,基本数据类型的局部变量,当值发生改变的时候,在栈中重新创建一个值,而不是修改一个值

2.基本数据类型的成员变量

public class Person{
private int age;
private String name;
private int grade;
//篇幅较长,省略setter getter方法
static void run(){
System.out.println("run....");
};
}

//调用
Person per=new Person();


基本类型的成员变量就是上面的 int age String name grade 是对象per中的内容,per对象存放在堆中,引用变量存放在栈中,值为地址

3.基本数据类型的静态变量
静态变量,成员变量,常量,字符串和类信息,静态方法,普通方法等存放在方法区中(方法区中常量池中一部分是字面量一部分是符号引用)
基本数据类型的静态变量存放在方法区的运行时常量池中,是字面量的内容,在编译期间已经完成,在类加载的时候存放在常量池中(随类的加载而加载,随类的消失而消失)

引用数据类型的存储

Person per=new Person();

Person per变量是引用类型的变量,存放在栈中,java虚拟机首先会在栈中的变量表中开辟一块空间存放per变量,在执行per =new Person()的时候,jvm会在堆中创建一个person类的实例对象,同时把实例地址赋值给栈中的引用变量

区分是存放在栈中还是方法区中,就看是(局部变量,创建对象的引用变量)还是(成员变量,静态变量,常量)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐