JVM基础系列第6讲:Java 虚拟机内存结构
看到这里,我相信大家对于一个 Java 源文件是如何变成字节码文件,以及字节码文件的含义已经非常清楚了。那么接下来就是让 Java 虚拟机运行字节码文件,从而得出我们最终想要的结果了。在这个过程中,Java 虚拟机会加载字节码文件,将其存入 Java 虚拟机的内存空间中,之后进行一系列的初始化动作,最后运行程序得出结果。
那么字节码数据在 Java 虚拟机内存中是如何存放的 ?Java 虚拟机在为类实例或成员变量分配内存是如何分配的 ?要解答上面这些问题,我们首先需要了解一下 Java 虚拟机的内存结构。
其实 Java 虚拟机的内存结构并不是官方的说法,在《Java 虚拟机规范》中用的是「运行时数据区」这个术语。但很多时候这个名词并不是很形象,再加上日积月累的习惯,我们都习惯用虚拟机内存结构这个说法了。

根据《Java 虚拟机规范》中的说法,Java 虚拟机的内存结构可以分为公有和私有两部分。公有指的是所有线程都共享的部分,指的是 Java 堆、方法区、常量池。私有指的是每个线程的私有数据,包括:PC寄存器、Java 虚拟机栈、本地方法栈。
公有部分:Java堆、方法区、常量池
在 Java 虚拟机中,线程共享部分包括 Java 堆、方法区及常量池。
Java 堆指的是从 JVM 划分出来的一块区域,这块区域专门用于 Java 实例对象的内存分配,几乎所有实例对象都在会这里进行内存的分配。之所以说几乎是因为有特殊情况,有些时候小对象会直接在栈上进行分配,这种现象我们称之为「栈上分配」。这里并不深入介绍,后续有章节会介绍。
方法区指的是存储 Java 类字节码数据的一块区域,它存储了每一个类的结构信息,例如运行时常量池、字段和方法数据、构造方法等。可以看到常量池其实是存放在方法区中的,但《Java 虚拟机规范》将常量池和方法区放在同一个等级上,这点我们知晓即可。
方法区在不同版本的虚拟机有不同的表现形式,例如在 1.7 版本的 HotSpot 虚拟机中,方法区被称为永久代(Permanent Space),而在 JDK 1.8 中则被称之为 MetaSpace。
说完这几个部分的大致作用之后,我们来深入说说 Java 堆。
Java 堆根据对象存活时间的不同,Java 堆还被分为年轻代、老年代两个区域,年轻代还被进一步划分为 Eden 区、From Survivor 0、To Survivor 1 区。如下图所示。

当有对象需要分配时,一个对象永远优先被分配在年轻代的 Eden 区,等到 Eden 区域内存不够时,Java 虚拟机会启动垃圾回收。此时 Eden 区中没有被引用的对象的内存就会被回收,而一些存活时间较长的对象则会进入到老年代。在 JVM 中有一个名为 -XX:MaxTenuringThreshold 的参数专门用来设置晋升到老年代所需要经历的 GC 次数,即在年轻代的对象经过了指定次数的 GC 后,将在下次 GC 时进入老年代。
这里让我们思考一个问题:为什么 Java 堆要进行这样一个区域划分呢?
根据我们的经验,虚拟机中的对象必然有存活时间长的对象,也有存活时间短的对象,这是一个普遍存在的正态分布规律。如果我们将其混在一起,那么因为存活时间短的对象有很多,那么势必导致较为频繁的垃圾回收。而垃圾回收时不得不对所有内存都进行扫描,但其实有一部分对象,它们存活时间很长,对他们进行扫描完全是浪费时间。因此为了提高垃圾回收效率,分区就理所当然了。
另外一个值得我们思考的问题是:为什么默认的虚拟机配置,Eden:from :to = 8:1:1 呢?
其实这是 IBM 公司根据大量统计得出的结果。根据 IBM 公司对对象存活时间的统计,他们发现 80% 的对象存活时间都很短。于是他们将 Eden 区设置为年轻代的 80%,这样可以减少内存空间的浪费,提高内存空间利用率。
私有部分:PC寄存器、Java 虚拟机栈、本地方法栈
Java 堆以及方法区的数据是共享的,但是有一些部分则是线程私有的。线程私有部分可以分为:PC 寄存器、Java 虚拟机栈、本地方法栈三大部分。
PC 寄存器,顾名思义 Program Counter 寄存器,指的是保存线程当前正在执行的方法。如果这个方法不是 native 方法,那么 PC 寄存器就保存 Java 虚拟机正在执行的字节码指令地址。如果是 native 方法,那么 PC 寄存器保存的值是 undefined。任意时刻,一条 Java 虚拟机线程只会执行一个方法的代码,而这个被线程执行的方法称为该线程的当前方法,其地址被存在 PC 寄存器中。
Java 虚拟机栈,这个栈与线程同时创建,用来存储栈帧,即存储局部变量与一些过程结果的地方。栈帧存储的数据包括:局部变量表、操作数栈。
当 Java 虚拟机使用其他语言(例如 C 语言)来实现指令集解释器时,也会使用到本地方法栈。如果 Java 虚拟机不支持 natvie 方法,并且自己也不依赖传统栈的话,可以无需支持本地方法栈。
总结
Java 虚拟机的内存结构是学习虚拟机所必须掌握的地方,其中以 Java 堆的内存模型最为重要,因为线上问题很多时候都是 Java 堆出现问题。因此掌握 Java 堆的划分以及常用参数的调整最为关键。
除了上述所说的六大部分之外,其实在 Java 中还有直接内存、栈帧等数据结构。但因为直接内存、栈帧的使用场景还比较少,所以这里并不做介绍,以免让初学者一时间混淆。
学到这里,一个 Java 文件就加载到内存中了,并且 Java 类信息就会存储在我们的方法区中。如果创建对象,那么对象数据就会存放在 Java 堆中。如果调用方法,就会用到 PC 寄存器、Java 虚拟机栈、本地方法栈等结构。那么面对如此之多的 Java 类,JVM 是如何决定这些类的加载顺序,又是如此控制它们的加载的呢?下一节,我们讲讲 JVM 的类加载机制。

如果只是看,其实无法真正学会知识的。为了帮助大家更好地学习,我建了一个虚拟机群,专门讨论学习 Java 虚拟机方面的内容,每周针对我所发文章进行讨论答疑。如果你有兴趣,关注「Java技术精选」公众号,通过右下角菜单「入群交流」加我好友,小助手会拉你入群。
JVM系列目录
- JVM基础系列开篇:为什么要学虚拟机?
- JVM基础系列第1讲:Java 语言的前世今生
- JVM基础系列第2讲:Java 虚拟机的历史
- JVM基础系列第3讲:到底什么是虚拟机?
- JVM基础系列第4讲:从源代码到机器码,发生了什么?
- JVM基础系列第5讲:字节码文件结构
- JVM基础系列第6讲:Java虚拟机内存结构
- JVM基础系列第7讲:JVM类加载机制
- JVM基础系列第8讲:JVM 垃圾回收机制
- JVM基础系列第9讲:JVM垃圾回收器
- JVM基础系列第10讲:垃圾回收的几种类型
- JVM基础系列第11讲:JVM参数之堆栈空间配置
- JVM基础系列第12讲:JVM参数之查看JVM参数
- JVM基础系列第13讲:JVM参数之追踪类信息
- JVM基础系列第14讲:JVM参数之GC日志配置
- JVM基础系列第15讲:JDK性能监控命令
JVM基础系列第6讲:Java 虚拟机内存结构的更多相关文章
- JVM基础系列第2讲:Java 虚拟机的历史
说起 Java 虚拟机,许多人就会将其与 HotSpot 虚拟机等同看待.但实际上 Java 虚拟机除了 HotSpot 之外,还有 Sun Classic VM.Exact VM.BEA JRock ...
- JVM基础系列第3讲:到底什么是虚拟机?
我们都知道在 Windows 系统上一个软件包装包是 exe 后缀的,而这个软件包在苹果的 Mac OSX 系统上是无法安装的.类似地,Mac OSX 系统上软件安装包则是 dmg 后缀,同样无法在 ...
- JVM基础系列第1讲:Java 语言的前世今生
Java 语言是一门存在了 20 多年的语言,其年纪比我自己还大.虽然存在了这么长时间,但 Java 至今都是最大的工业级语言,许多大型互联网公司均采用 Java 来实现其业务系统.大到国际电商巨头阿 ...
- JVM基础系列第15讲:JDK性能监控命令
查看虚拟机进程:jps 命令 jps 命令可以列出所有的 Java 进程.如果 jps 不加任何参数,可以列出 Java 程序的进程 ID 以及 Main 函数短名称,如下所示. $ jps 6540 ...
- JVM基础系列第14讲:JVM参数之GC日志配置
说到 Java 虚拟机,不得不提的就是 Java 虚拟机的 GC(Garbage Collection)日志.而对于 GC 日志,我们不仅要学会看懂,而且要学会如何设置对应的 GC 日志参数.今天就让 ...
- JVM基础系列第13讲:JVM参数之追踪类信息
我们都知道 JVM 在启动的时候会去加载类信息,那么我们怎么得知他加载了哪些类,又卸载了哪些类呢?我们这一节就来介绍四个 JVM 参数,使用它们我们就可以清晰地知道 JVM 的类加载信息. 为了方便演 ...
- JVM基础系列第11讲:JVM参数之堆栈空间配置
JVM 中最重要的一部分就是堆空间了,基本上大多数的线上 JVM 问题都是因为堆空间造成的 OutOfMemoryError.因此掌握 JVM 关于堆空间的参数配置对于排查线上问题非常重要. tips ...
- JVM基础系列第10讲:垃圾回收的几种类型
我们经常会听到许多垃圾回收的术语,例如:Minor GC.Major GC.Young GC.Old GC.Full GC.Stop-The-World 等.但这些 GC 术语到底指的是什么,它们之间 ...
- JVM基础系列第9讲:JVM垃圾回收器
前面文章中,我们介绍了 Java 虚拟机的内存结构,Java 虚拟机的垃圾回收机制,那么这篇文章我们说说具体执行垃圾回收的垃圾回收器. 总的来说,Java 虚拟机的垃圾回收器可以分为四大类别:串行回收 ...
随机推荐
- Esptouch移植xamarin记要
目标: 1.支持最新esptouch库更新 2.android和IOS双端混合UI开发App 解决思路: 到esptouch官方开源项目库(android,ios)编译jar及ios标准库到xamar ...
- 回文自动机(PAM) 学习笔记
原文链接www.cnblogs.com/zhouzhendong/p/PAM.html 前置知识 无. (强行说和KMP有关也是可以的……) 关于回文串的一些性质 1. 一个长度为 n 的字符串最多有 ...
- pygame学习之绘制移动的矩形
import pygame from pygame.locals import * pygame.init() screen = pygame.display.set_mode((600, 500)) ...
- html网页调用本地exe程序
1.使用记事本(或其他文本编辑器)创建一个protocal.reg文件,并写入以下内容 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\ ...
- PCB Mark点相关
1)Mark点用于锡膏印刷和元件贴片时的光学定位.根据Mark点在PCB上的作用,可分为拼板Mark点.单板Mark点.局部Mark点(也称器件级MARK点) 2)拼板的工艺边上和不需拼板的单板上应至 ...
- 关于resharper激活
resharper 是一款非常强大的vs辅助开发插件,提供了很多快捷操作功能,本人已经离不开它了,但是resharper总会遇到lincese过期,需要激活的问题,现在提供以下方式,仅供参考 1.打开 ...
- 通过源码理解HashMap的并发问题
最近在学习有关于Java的基础知识,在学习到HashMap的相关知识的时候,了解了HashMap的并发中会出现的问题,在此记录,加深理解(这篇文章是基于Java1.7的,主要是为了更加直观,更新版本的 ...
- ubuntu系统下matplotlib中文乱码问题
参考 [ubuntu系统下matplotlib中文乱码问题 - CSDN博客](https://blog.csdn.net/jeff_liu_sky_/article/details/54023745 ...
- PHP序列号生成函数和字符串替换函数代码
/** * 序列号生成器 */ function snMaker($pre = '') { $date = date('Ymd'); $rand = rand(1000000,9999999); $t ...
- java中的异常(二)
异常的分类 在使用上 Error不用管他虚拟机错误 Exception必须要用catch抓 RuntimeExcption可以处理也可以不用处理 说明 Error:称为错误,由java虚拟机生成并抛出 ...