文章目录
1. JVM内存模型
在JVM引言中,我们提到操作系统给JVM分配了一套内存区域,由JVM负责在Java程序运行时进行管理,并且在类加载时也使用了方法区(静态区)存储类的静态变量,那么,内存中其他区域呢?分别是用来干什么的呢?
1.1 JVM内存区域的划分
在学习操作系统时,我们学习了进程与线程,了解了进程的工作模式、线程的各种概念及线程间共享,让我们带着这些知识来了解JVM内存区域。
Java程序虽然形式上是运行在JVM上的,但是其本质上还是作为一个进程运行在操作系统上,因此操作系统给JVM分配的内存区可以按进程的内存区来理解,而Java程序提供多线程编程,那么从逻辑上,JVM内存区必然要为线程间通信提供物理基础,也就是线程共享区,当然,也要保留线程私有区来存储线程安全的数据。
而根据Java语言的特点,共享区还需要开辟一个空间给静态变量使用,也就是前面提到的方法区,剩下的用来存储运行时线程共享的对象,也就是Java堆。
同样,线程私有区需要存储线程执行时各种函数调用,用虚拟机栈存储调用的具体信息。并且线程私有区还需要根据线程的特点开辟一个特别区域用以线程切换和执行:程序计数器,此外,在程序执行期间,还可能调用其他语言提供的函数(java的native函数),而这些函数与Java函数类似但又不同,因此有的JVM特定开辟出一段内存空间存储这里方法的数据:本地方法栈。
1.1.1 方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据 。
1.1.2 堆区
创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域(线程私有区随线程结束而销毁,方法区存储静态变量在程序运行期全过程有用)。
1.1.3 程序计数器
存储当前线程正在执行的字节码指令,是唯一一个在虚拟机中没有规定任何 OutOfMemoryError
情况的区域。
1.1.4 虚拟机栈
是描述java方法执行的内存模型,虚拟机会给每个线程分配一个线程栈,而每个方法在执行的同时都会分配一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
栈帧的内部:
- 局部变量表:
- 操作数栈:
- 动态链接:将符号引用在程序运行过程中转变为直接引用
- 方法出口:
1.1.5 本地方法栈
存储调用的本地(native)方法执行过程的信息
1.2 JVM与系统内存的关系
前面提到,JVM负责管理Java程序运行中的内存管理,但这是逻辑内存,实际上操作的还是计算机上的真实内存。
因此,JVM内存模型提出了一套机制来实现内存管理。
1.2.1 线程间通信
上面讲到了Java线程之间的通信采用的是过共享内存,其实JMM规定:线程间的共享变量存储主存中,每个线程都有一个本地内存,本地内存中存储了该线程以读/写共享变量的副本。
我对上句话的理解是,共享内存分为堆区和方法区,其中堆区存储的是对象,本地线程通过持有对象的指针来操作对象,无需保存副本。而方法区的静态对象,才是上面一句话的对象,就是说,线程并不是直接操纵方法区的对象,而是通过操纵本地副本,然后再更新静态对象。
前面提到,线程使用数据采用拷贝模式,因此必然存在多线程下数据修改干扰,而**数据可见性指的是当一个共享变量在多个本地内存中有副本时,如果一个本地内存修改了该变量的副本,其他变量应该能够看到修改后的值,此为可见性。**可想而知,数据可见性必然通过JVM控制本地内存与主内存进行交互实现。
1.3 其他
off-heap指堆外内存,将你的对象从堆中脱离出来序列化,然后存储在一大块内存中,这就像它存储到磁盘上一样,但它仍然在RAM中。对象在这种状态下不能直接使用,它们必须首先反序列化,也不受垃圾收集。序列化和反序列化将会影响部分性能(所以可以考虑使用FST-serialization)使用堆外内存能够降低GC导致的暂停。堆外内存不受垃圾收集器管理,
也不属于老年代,新生代。