Java8 内存结构图
虚拟机内存与本地内存的区别
Java 虚拟机在执行的时候会把治理的内存调配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有间接治理的物理内存,也有肯定的利用,这些被利用却不在虚拟机内存数据区的内存,咱们称它为本地内存,这两种内存有肯定的区别:
JVM 内存
- 受虚拟机内存大小的参数管制,当大小超过参数设置的大小时就会报 OOM
本地内存
- 本地内存不受虚拟机内存参数的限度,只受物理内存容量的限度
- 尽管不受参数的限度,然而如果内存的占用超出物理内存的大小,同样也会报 OOM
java 运行时数据区域
java 虚拟机在执行过程中会将所治理的内存划分为不同的区域,有的随着线程产生和隐没,有的随着 java 过程产生和隐没,依据《Java 虚拟机标准》的规定,运行时数据辨别为以下一个区域:
程序计数器(Program Counter Register)
程序计数器就是以后线程所执行的字节码的行号指示器,通过扭转计数器的值,来选取下一行指令,通过他来实现跳转、循环、复原线程等性能。
- 在任何时刻,一个处理器内核只能运行一个线程,多线程是通过线程轮流切换,调配工夫来实现的,这就须要有一个标记来记住每个线程执行到了哪里,这里便须要到了程序计数器。
- 所以,程序计数器是线程公有的,每个线程都已本人的程序计数器。
虚拟机栈(JVM Stacks)
虚拟机栈是线程公有的,随线程生灭。虚拟机栈形容的是线程中的办法的内存模型:
每个办法被执行的时候,都会在虚拟机栈中同步创立一个栈帧(stack frame)。
每个栈帧的蕴含如下的内容
- 局部变量表
-
- 局部变量表中存储着办法里的 java 根本数据类型(byte/boolean/char/int/long/double/float/short)以及对象的援用(注:这里的根本数据类型指的是办法内的局部变量)
- 操作数栈
- 动静连贯
- 办法返回地址
办法被执行时入栈,执行完后出栈
虚拟机栈可能会抛出两种异样:
- 如果线程申请的栈深度大于虚拟机所规定的栈深度,则会抛出 StackOverFlowError 即栈溢出
- 如果虚拟机的栈容量能够动静扩大,那么当虚拟机栈申请不到内存时会抛出 OutOfMemoryError 即 OOM 内存溢出
本地办法栈(Native Method Stacks)
本地办法栈与虚拟机栈的作用是类似的, 都会抛出 OutOfMemoryError 和 StackOverFlowError,都是线程公有的,次要的区别在于:
- 虚拟机栈执行的是 java 办法
- 本地办法栈执行的是 native 办法(什么是 Native 办法?)
Java 堆(Java Heap)
java 堆是 JVM 内存中最大的一块,由所有线程共享, 是由垃圾收集器治理的内存区域,次要寄存对象实例,当然因为 java 虚拟机的倒退,堆中也多了许多货色,当初次要有:
- 对象实例
-
- 类初始化生成的对象
- 根本数据类型的数组也是对象实例
- 字符串常量池
-
- 字符串常量池本来寄存于办法区,jdk7 开始搁置于堆中。
- 字符串常量池存储的是 string 对象的间接援用,而不是间接寄存的对象,是一张 string table
- 动态变量
-
- 动态变量是有 static 润饰的变量,jdk7 时从办法区迁徙至堆中
- 线程调配缓冲区(Thread Local Allocation Buffer)
-
- 线程公有,然而不影响 java 堆的共性
- 减少线程调配缓冲区是为了晋升对象调配时的效率
java 堆既能够是固定大小的,也能够是可扩大的(通过参数 -Xmx 和 -Xms 设定),如果堆无奈扩大或者无奈分配内存时也会报 OOM。
办法区(Method Area)
办法区相对是网上所有对于 java 内存构造文章争执的焦点,因为办法区的实现在 java8 做了一次大变革,当初咱们来讨论一下:
办法区是所有线程共享的内存,在 java8 以前是放在 JVM 内存中的,由永恒代实现,受 JVM 内存大小参数的限度,在 java8 中移除了永恒代的内容,办法区由元空间 (Meta Space) 实现,并间接放到了本地内存中,不受 JVM 参数的限度(当然,如果物理内存被占满了,办法区也会报 OOM),并且将原来放在办法区的字符串常量池和动态变量都转移到了 Java 堆中,办法区与其余区域不同的中央在于,办法区在编译期间和类加载实现后的内容有少许不同,不过总的来说分为这两局部:
类元信息(Klass)
- 类元信息在类编译期间放入办法区,外面搁置了类的根本信息,包含类的版本、字段、办法、接口以及常量池表(Constant Pool Table)
- 常量池表(Constant Pool Table)存储了类在编译期间生成的字面量、符号援用(什么是字面量?什么是符号援用?),这些信息在类加载完后会被解析到运行时常量池中
运行时常量池(Runtime Constant Pool)
- 运行时常量池次要寄存在类加载后被解析的字面量与符号援用,但不止这些
- 运行时常量池具备动态性,能够增加数据,比拟多的应用就是 String 类的 intern()办法
间接内存
间接内存位于本地内存,不属于 JVM 内存,然而也会在物理内存耗尽的时候报 OOM, 所以也讲一下。
在 jdk1.4 中退出了 NIO(New Input/Putput)类,引入了一种基于通道(channel)与缓冲区(buffer)的新 IO 形式,它能够应用 native 函数间接调配堆外内存,而后通过存储在 java 堆中的 DirectByteBuffer 对象作为这块内存的援用进行操作,这样能够在一些场景下大大提高 IO 性能,防止了在 java 堆和 native 堆来回复制数据。
常见问题
什么是 Native 办法?
因为 java 是一门高级语言,离硬件底层比拟远,有时候无奈操作底层的资源,于是,java 增加了 native 关键字,被 native 关键字润饰的办法能够用其余语言重写,这样,咱们就能够写一个本地办法,而后用 C 语言重写,这样来操作底层资源。当然,应用了 native 办法会导致系统的可移植性不高,这是须要留神的。
成员变量、局部变量、类变量别离存储在内存的什么中央?
类变量
- 类变量是用 static 修饰符润饰,定义在办法外的变量,随着 java 过程产生和销毁
- 在 java8 之前把动态变量寄存于办法区,在 java8 时寄存在堆中
成员变量
- 成员变量是定义在类中,然而没有 static 修饰符润饰的变量,随着类的实例产生和销毁,是类实例的一部分
- 因为是实例的一部分,在类初始化的时候,从运行时常量池取出间接援用或者值,与初始化的对象一起放入堆中
局部变量
- 局部变量是定义在类的办法中的变量
- 在所在办法被调用时放入虚拟机栈的栈帧中,办法执行完结后从虚拟机栈中弹出,所以寄存在虚拟机栈中
由 final 润饰的常量寄存在哪里?
final 关键字并不影响在内存中的地位,具体位置请参考上一问题。
类常量池、运行时常量池、字符串常量池有什么关系?有什么区别?
类常量池与运行时常量池都存储在办法区,而字符串常量池在 jdk7 时就曾经从办法区迁徙到了 java 堆中。
在类编译过程中,会把类元信息放到办法区,类元信息的其中一部分便是类常量池,次要寄存字面量和符号援用,而字面量的一部分便是文本字符,在类加载时将字面量和符号援用解析为间接援用存储在运行时常量池;
对于文本字符来说,它们会在解析时查找字符串常量池,查出这个文本字符对应的字符串对象的间接援用,将间接援用存储在运行时常量池;字符串常量池存储的是字符串对象的援用,而不是字符串自身。
什么是字面量?什么是符号援用?
字面量
java 代码在编译过程中是无奈构建援用的,字面量就是在编译时对于数据的一种示意:
int a=1;// 这个 1 便是字面量
String b="iloveu";//iloveu 便是字面量
符号援用
因为在编译过程中并不知道每个类的地址,因为可能这个类还没有加载,所以如果你在一个类中援用了另一个类,那么你齐全无奈晓得他的内存地址,那怎么办,咱们只能用他的类名作为符号援用,在类加载完后用这个符号援用去获取他的内存地址。
例子:我在 com.demo.Solution 类中援用了 com.test.Quest,那么我会把 com.test.Quest 作为符号援用存到类常量池,等类加载完后,拿着这个援用去办法区找这个类的内存地址。
原文链接:https://blog.csdn.net/qq_3562…
版权申明:本文为 CSDN 博主「lei6393」的原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接及本申明。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)
2. 别在再满屏的 if/ else 了,试试策略模式,真香!!
3. 卧槽!Java 中的 xx ≠ null 是什么新语法?
4.Spring Boot 2.5 重磅公布,光明模式太炸了!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!