JavaJava之内存结构
Posted Rose J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaJava之内存结构相关的知识,希望对你有一定的参考价值。
Java的内存结构分为线程共享区域和线程私有区域,
线程共享区分为:方法区和堆区
线程私有区分为:栈区(虚拟机栈和本地方法栈),程序计数器
堆区
提供所有类实例和数组对象存储区域,属于线程共享区域
方法区
跟堆区一样,方法区也属于共享区域,方法区中存放着所有class文件及static变量,常量池也是在方法区中。 (类信息:版本,字段,方法,接口)
栈区
栈区属于线程私有,栈中只存储基本数据类型和自定义对象的引用
(栈中存放的是自定义对象的引用,对象是在堆区中)
栈又可以分为虚拟机栈,本地方法栈
虚拟机栈
虚拟机栈描述是Java方法执行的内存模型,每个方法执行时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
每一个栈帧中又包含了这些信息:
1.局部变量表:表长度在编译阶段确认,调用方法时传递的参数,以及在方法内部创建的局部变量都保存在局部变量表中
2.操作数栈: 也称为操作栈,是一个后入先出栈(LIFO),操作数栈的深度也是在编译阶段确定。当一个方法刚开始执行时,这个方法的操作数栈是空的,在方法执行过程中,会有各种字节码指令被压入和弹出操作数栈。
3.动态链接: 主要是为了支持方法调用过程中的动态连接。比如在一个方法中去调用其他方法。
4.返回地址: 用来帮助当前方法恢复它的上层方法执行状态。
本地方法栈
同虚拟机栈基本相同,区别在于虚拟机栈是为Java方法服务,本地方法栈是为虚拟机栈中使用到的Native栈服务。
程序计数器
线程私有,程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。字节码解释器可以通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程 恢复等都依赖这个计数器完成。每个线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储。
如果线程正在执行的是一个Java方法,这个线程对应的计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的是Native方法,这个计数器值为空(Undefined)。程序计数器所在的内存区域是唯一一个没有规定任何OutOfMemoryError情况的区域,生命周期随线程的创建而创建,随线程的消失而死亡。
PS: 我们开发中常见的两个异常:
StackOverflowError:栈溢出异常,原因是因为线程请求的深度大于虚拟机所允许的深度。一般无限递归会产生该异常,因为每调用一次递归的方法时,都会在虚拟机栈中创建一个栈帧,且方法不会退出,因为是无限循环,当超过虚拟机允许的最大深度时,就会抛出该异常。
OutOfMemoryError:内存溢出异常,理论上在堆、栈、方法区都有发生OOM的可能,但是实际中一般发生在堆中,当前堆中需要申请的内存+已使用的内存>虚拟机分配的内存时,就会导致内存不足,进而抛出该异常。
面试注意
1.基本数据类型存在哪里?
栈区
2.string类型存在哪里?
java的String是一个很神奇的数据类型,因为它有两种声明方法:
String a = “abc”;
String b = new String(“abc”);
这两种声明方法得到的String是不一样的,第一种声明方法得到的String是存储在String常量池里面的,存在方法区中
而第二种方式的声明很明显是正常的创建一个对象的使用方法,所以存储在堆中。
3.对象存储在哪里?
对象是在堆区中,栈中存放的是自定义对象的引用
4.静态变量存在哪里?
方法区
5.局部变量存在哪里?
栈区,虚拟机栈中
6.实际变量存在哪里?
实例变量属于某个实例对象的属性,必须创建了实例对象,其中的实例变量才会被创建并且分配空间,进而才能使用这个实例变量。 所以存在堆中
以上是关于JavaJava之内存结构的主要内容,如果未能解决你的问题,请参考以下文章