目录
◆ JVM体系结构
◆
程序计数器
◆
虚拟机栈
◆
本地方法栈
◆
堆
◆
方法区
1.什么是JVM
Java Virtual Machine:Java虚拟机,用来保证Java语言跨平台。
Java虚拟机可以看做是一台抽象的计算机,如同真实的计算机那样,它有自己的指令集以及各种运行时内存区域。
Java虚拟机与Java语言并没有必然的联系,它只与特定的二进制文件格式(class文件格式所关联)
Java 虚拟机就是一个字节码翻译器,它将字节码文件翻译成各个系统对应的机器码,确保字节码文件能在各个系统正确运行。
2.JVM体系结构
![](https://i-blog.csdnimg.cn/blog_migrate/030efd72eed553e95612fb46ab885540.png)
3.JVM内存结构
Java SE 8版本的虚拟机规范地址:
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
![](https://i-blog.csdnimg.cn/blog_migrate/ceb1d59f800cb7cb0f9eed84d1dde956.png)
主要讲述JVM内存结构的五块内存:程序计数器,虚拟机栈,本地方法栈,堆,方法区。
4.程序计数器
程序计数器:程序计数器是一块内存空间,属于线程独有的,每个线程都有一个独有的程序计数器,保存当前执行指令的地址,确保在CPU在多线程中切换时,不会因为来回切换线程导致原线程错误执行之前的指令。
作用:保存当前执行指令的地址,一旦指令执行,程序计数器将更新到下一条指令。
![](https://i-blog.csdnimg.cn/blog_migrate/fc785ebff3d010fe1a8a3b88e41a1a02.png)
5.虚拟机栈
每个线程运行时所需要的内存空间,称为虚拟机栈,所以虚拟机栈是线程私有的,线程安全。
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。
![](https://i-blog.csdnimg.cn/blog_migrate/77e55cfafac3c73ac8101dda61bba836.png)
栈帧
栈帧的组成部分:局部变量表、操作数栈、动态链接、方法返回地址
(1)局部变量表:
存放局部变量的列表,
一个局部变量可以保存类型为boolean、byte、char、
short、float、reference和returnAddress的数据,
两个局部变量可以保存一个类型为long和double
的数据,
局部变量使用索引来进行定位访问,第一个局部变量的索引值为零
(2)操作数栈:
也称为操作栈,它是一个后进先出的栈。
当一个方法刚刚开始执行时,其操作数栈是空的,
随着方法执行和字节码指令的执行,会从局部变
量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将
栈中元素出栈到
局部变量表或者返回给方法调用者,也就是出栈/入栈操作。
一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。
简单理解, 操作数栈是线程实际的操作台。
(3)动态链接:
简单的理解为指向运行时常量池的引用。
在class文件里面,描述一个方法调用了其他方法,或者访问其成员变量是通过符号引用来表示
的,动态链接的作用就是将这些符
号引用所表示的方法转换为实际方法的直接引用
(4)方法返回地址:
方法法调用的返回,包括正常返回(有返回值)和异常返回(没有返回值),不同的返回类型有不
同的指令。
无论方法采用何种方式退出,在方法退出后都需要返回到方法被调用的位置,程序才能继续执行,
方法返回时可能需要在当前栈
帧中保存一些信息,用来帮他恢复它的上层方法执行状态
栈内存溢出
因为栈帧问题会导致内存溢出的两种情况:
(1)栈帧过多导致栈内存溢出。
案例:无出口的递归,会引起栈帧过多而导致内存溢出。
![](https://i-blog.csdnimg.cn/blog_migrate/fed13268a53dca1f2d3c1476a30ee1a5.png)
(2)栈帧过大导致栈内存溢出。
栈内存溢出报异常:java.lang.StackoverflowError。
为了解决栈内存溢出的问题,可以设置栈的大小: -Xss size
比如下图,即设置栈大小为256k。
![](https://i-blog.csdnimg.cn/blog_migrate/ceb8cab41a43c48de793e2a2adbb4f49.png)
6.本地方法栈
本地方法栈的功能和特点类似于虚拟机栈,也是线程私有的。
不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的Java
方法。
如何去服务native方法?
native方法使用什么语言实现?
怎么组织像栈帧这种为了服务方法的数据结构?
虚拟机规范并未给出强制规定,因此不同的虚拟机实可以进行自由实现。
7.堆
作用:堆是用于存放对象的内存区域
特点:
堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
堆的区域是用来存放对象实例的,因此也是垃圾收集器管理的主要区域。
堆在逻辑上划分为“新生代”和“老年代”,新生代分为Eden区、ServivorFrom、ServivorTo三个区。
堆一般实现成大小是可扩展的,使用“-Xms”与“-Xmx”控制堆的最小与最大内存
![](https://i-blog.csdnimg.cn/blog_migrate/d8264822a1b0ca158e0de6c3cb79fa64.png)
堆内存溢出
堆内存溢出报异常:
java.lang.OutOfMemoryError: Java heap space
错误原因:
内存真不够,通过调整堆内存大小解决。
存在死循环,通过修改代码解决。
堆内存诊断
jps:查看当前系统中有哪些Java进程
jmap 工具:查看堆内存占用情况(某一个时刻)
jmap -heap 进程id
jconsole 工具:图形界面的,内置 Java 性能分析器,多功能的监测工具,可以连续监测
8.方法区
作用:存储每个类的结构
例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及
接口初始化的特殊方法等。
JDK8相对于JDK6,对于方法区做了很大的优化:
![](https://i-blog.csdnimg.cn/blog_migrate/d2c0214a250385e45e5de03feced4845.png)
方法区内存溢出
Java 8中演示元空间内存溢出:
java.lang.OutOfMemoryError: Metaspace
-XX:MaxMetaspaceSize=10m -XX:-UseCompressedOops
Java 6中演示永久代内存溢出:
java.lang.OutOfMemoryError: PermGen space
-XX:MaxPermSize=8m
注:JVM内存结构中主要有五个部分:
(1)程序计数器
(2)虚拟机栈,
(3)本地方法栈
(4)堆,
(5)方法区
程序计数器,虚拟机栈,本地方法栈都是线程私有的内存区域,线程安全。
而堆,方法区是所有线程共享的一块内存区域,线程不安全。