一文搞懂JVM内存结构,程序员必须掌握的知识
jvm内存结构组成
Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
简要言之,jmm是jvm的一种规范,定义了jvm的内存模型。它屏蔽了各种硬件和操作系统的访问差异,不像c那样直接访问硬件内存,相对安全很多,它的主要目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。可以保证并发编程场景中的原子性、可见性和有序性。
从下图可以看出,java内存模型分为五大数据区域,这些区域都有各自的用途以及他们的创建时间和销毁时间
其中方法区和堆是所有线程共享的,栈,本地方法栈和程序虚拟机则为线程私有的。
程序计数器(PC寄存器)
程序计数器是一块很小的区域,它是线程独占区域,可以认为它是线程行号的指示器。
栈空间
同程序计数器一样是线程独占区域
每个方法被执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法返回地址等信息,每一个方法被调用的过程就是对应一个栈帧从虚拟机入栈到出栈过程(栈是先进后出)
看如下图,可以知道代码执行是先从main方法执行,入栈开始,然后调用add方法,add方法在进入入栈,执行完毕之后add出栈,最后才main方法出栈,所以着对应着一个栈是先进后出的结果(一个方法对应一个栈帧)
且对于栈帧的详细解释参考 Java虚拟机运行时栈帧结构
栈帧:是存储数据结构,以及部分过程结果
栈帧的位置: 内存 -> 运行时数据区 -> 某个线程对应的虚拟机栈 -> here[在这里]
栈帧大小确认时间: 是在编译的时候就确认好了,不会在运行的时候受到数据变化的影响
需要注意的是,局部变量表所需要的空间大小是在编译的过程中就已经分配好了,当进入一个方法的时候,在栈中所需要的局部变量表的空间是完成确认的,在方法运行期间是不会受到数据的影响
Java虚拟机栈可能出现两种类型的异常:
1.线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
2.虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。
本地方法栈
本地方法栈与虚拟机栈的作用其实相差不大,最大一个区别就是虚拟机栈执行的是java方法(也就是字节码),而本地方法栈则为虚拟机使用的native方法,native方法主要是调用的是c或者c++
可以用过unsafe类查看这些方法
堆空间
对于大多数应用来说,堆是java虚拟机中最大的一块区域,因为他存储的的对象线程是共享的,所以在多线程情况下也需要进行同步机制。
且主要存储就是对象本身以及数组
方法区
方法区跟堆一样,是所有线程共享的区域,在jdk8中叫元数据区域
用于存储每个类的信息(类的名称,方法信息,字段信息)、静态变量、常量、以及编译器编译之后的代码等
(注:在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。)
注意:在老版jdk,方法区也被称为永久代【因为没有强制要求方法区必须实现垃圾回收,HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。】
jdk1.7开始逐步去永久代。从String.interns()方法可以看出来 String.interns() native方法:作用是如果字符串常量池已经包含一个等于这个String对象的字符串,则返回代表池中的这个字符串的String对象,在jdk1.6及以前常量池分配在永久代中。可通过 -XX:PermSize和-XX:MaxPermSize限制方法区大小。
static String base = "test";
/**
* 方式一:需要全局变量,不需要设定堆空间大小
* @param args
*/
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String str = base + base;
base = str;
list.add(str.intern());
}
}
/**
* 方式二:需要设定堆空间大小,不需要全局变量
*/
public static void main(String[] args) {
//用list保持着引用 防止full gc回收常量池
List<String> list = new ArrayList<String>();
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
//以上其中一个即可测试
//如果在jdk1.6环境下运行 同时限制方法区大小 将报OOM后面跟着PermGen space说明方法区OOM,即常量池在永久代
//如果是jdk1.7或1.8环境下运行 同时限制堆的大小 将报heap space 即常量池在堆中
这边暂时不用全局的方式,设置main方法的vm参数即可 做相关的设置,比如说设定堆大小(-Xmx5m -Xms5m -XX:-UseGCOverheadLimit)
这边如果不设置UseGCOverheadLimit将报java.lang.OutOfMemoryError: GC overhead limit exceeded,
这个错是因为GC占用了多余98%(默认值)的CPU时间却只回收了少于2%(默认值)的堆空间。目的是为了让应用终止,给开发者机会去诊断问题。一般是应用程序在有限的内存上创建了大量的临时对象或者弱引用对象,从而导致该异常。虽然加大内存可以暂时解决这个问题,但是还是强烈建议去优化代码,后者更加有效,也可通过UseGCOverheadLimit避免[不推荐,这里是因为测试用,并不能解决根本问题]
jdk8真正开始废弃永久代,转而使用元空间(Metaspace)
java虚拟机对方法区比较宽松,除了跟堆一样可以不存在连续的内存空间,定义空间和可扩展空间,还可以选择不实现垃圾收集。
- 开发者的如何优雅的使用OSX
- Solidity 智能合约开发语言·数据类型
- 以太坊·Rinkeby 测试网络
- TensorFlow强化学习入门(0)——Q-Learning的查找表实现和神经网络实现
- 【云端架构】网站运维之CDN链接鉴权示例入门(PHP)
- 以太坊·单机多实例演示
- TensorFlow强化学习入门(1)——双臂赌博机
- CTF逆向--.NET与Python篇
- CTF逆向--安卓篇
- hackme.inndy.tw的19道web题解(下)
- hackme.inndy.tw的19道web题解(中)
- hackme.inndy.tw的19道web题解(上)
- CTF中RSA题型解题思路及技巧
- 技术解析 | Web缓存欺骗测试
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 详细分析Python可变对象和不可变对象
- spring-boot-route(十二)整合redis做为缓存
- ThinkPHP框架实现的微信支付接口开发完整示例
- spring-boot-route(十三)整合RabbitMQ消息队列
- spring-boot-route(十四)整合Kafka
- laravel 事件/监听器实例代码
- pytorch 多分类问题,计算百分比操作
- spring-boot-route(十五)整合RocketMQ
- spring-boot-route(十六)使用logback生产日志文件
- 你真的知道怎么实现一个延迟队列吗 ?
- 腾讯云容器服务日志采集最佳实践
- 深度剖析Lottie动画原理
- spring-boot-route(十七)使用aop记录操作日志
- spring-boot-route(十八)spring-boot-actuator监控应用
- 浅析PHP7的多进程及实例源码