聊一聊Java内存模型

Java内存模型是所有Java程序正常运行的基础,了解清楚Java内存模型对于我们熟悉Java程序运行机制有着至关重要的作用。今天我们来聊一聊Java内存模型,看一下Java程序究竟是如何运行起来的。

在网上会经常看到这张图,详细介绍了Java程序在运行的时候,每一块使用到的内存是什么样的。

Java内存模型

今天的Jvm运行时所使用的的内存模型基本没有变化,接下来我们分别介绍一下每个内存区域的作用和特点。

总体上Java内存可以分为两种:线程共享内存线程私有内存。因为在Java程序运行的时候,资源有共享和私有两种情况。

线程共享内存

线程共享内存包括Java堆和方法区,这部分内存的分配和回收都是动态,而垃圾回收器也关注这一块的内存。

Java 堆 (Java Heap)

Java 堆 是JVM运行时占用最大的一块内存区域,这里主要存放着对象实例,所有通过new Object() 产生的对象都会存储在Java 堆中。

JVM在触发垃圾回收(Garbage Collected, GC)机制时,主要是发生在Java 堆中。

Java 堆在内存使用耗尽后,JVM会抛出OutOfMemoryError异常。

GC优化

GC过程会耗费一定时间的,称为, 为了提高GC的效率,JVM将堆内存按比例分为新生代和老年代两块。

新生代内存

新生代内存(Eden Area) ,对象实例初始化完成后,会存储在新生代内存中。

当新生代内存空间不足时,会触发一次Minor GC操作。

根据2-8定理,新生代内存中80%的对象实例会被回收掉,剩下的20%会一直存在于内存中,因此经过几轮GC后,JVM会将实例对象复制到老年代内存中。

Eden,源自于圣经中的伊甸园,神·耶和华照自己的形像创造了人类的祖先,男人亚当,再用亚当的一根肋骨创造了女人夏娃,并安置这对男女住在伊甸园中。

老年代内存

如果实例化了大对象,也会直接放到老年代内存中存储。

对象在Eden区出生后,经过第一次Minor GC后仍然存活,就会被存储到Survivor区域,且对象年龄设置为1。在Survivor区域每经历一次Minor GC,年龄+1,一直到年龄=15,JVM就会将对象复制到老年代中。

老年代内存储的对象实例会经常被引用到,所以JVM触发GC的频率相对于新生代要低。

方法区(Method Area)

方法区存储着已被JVM记载的类信息、常量、静态变量,以及编译期编译后的代码等数据。

运行时常量池(Runtime Constant Pool) 是方法区的一部分。

运行时常量池(Runtime Constant Pool)

Class 文件中除了具有类的版本,字段,方法,接口等信息外,还有一个是常量池(Constant Pool Table),用于存放编译期生成的字面量和符号引用。常量池的内容将在类加载后存放在运行时常量池。

  • 字面量 如字符串,声明为final的常量值等。
  • 符号引用 包括类和接口的全限定名,即路径 = 包名+类名;字段的名称和描述符,方法的名称和描述符。

方法区在内存使用耗尽后,JVM会抛出OutOfMemoryError异常。

线程私有内存

线程私有内存,包括程序计数器,Java虚拟机栈,本地方法栈3个区域,它们随着线程的产生而产生,随着线程的结束而回收。

Java 虚拟机栈 (Java Virtual Machine Stacks)

Java 虚拟机栈描述的是Java方法执行的内存模型。

每个方法在执行的时候都会创建一个栈帧,用来存储局部变量、操作数栈、动态链接、方法出口等信息。

一个栈帧随着一个方法的调用开始而创建(入栈操作),方法的完成而销毁(出栈操作)。

本地方法栈栈帧

Java虚拟机栈会抛出两种异常:

  • StackOverFlowError, 程序需要申请的栈内存超过了设置的栈内存大小;
  • OutOfMemoryError, JVM申请的内存大小超过了可用内存时抛出异常;

本地方法栈 (Native Method Stacks)

本地方法栈为虚拟机使用到的Native方法服务,发挥的作用与Java虚拟机栈类似。

Java Native 方法: Java调用非Java代码的接口。

感觉这个中文翻译名称一点也不好,“本地方法栈”很容易让人产生误解,直接用“Native方法栈”一眼就看出是与Native服务相关的内存。因此还是要多看一些英文文档,方便加深记忆。

与Java虚拟机栈类似,本地方法栈也会抛出两种异常: StackOverFlowError和OutOfMemoryError。

程序计数器 (Program Counter Register)

程序计数器是一块较小的内存空间,字节码解释器通过程序计数器的值来选择下一个需要执行的字节码指令。

分支、循环、跳转、异常处理以及线程恢复都需要依赖程序计数器来完成。

总结

了解JVM内存模型是写好Java程序的第一步,知道Java程序在运行过程中是如何分配内存,出了问题如何调整。

JVM内存模型主要分为两大类:线程私有内存和线程共享内存。

线程私有内存包含:Java虚拟机栈,本地方法栈和程序计数器;

线程共享内存包含:Java堆和方法区。

Java栈和堆的的划分是很粗糙的划分方式。

主要内容总结在下图。
Java内存模型知识图谱

最近在威海生活,丝毫感觉不到炎热的夏季,每天睡觉都还有点冷。

祝大家早安,午安,晚安……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值