先来看看JVM运行时候的内存区域,如下图:

  大多数 JVM 将内存区域划分为 Heap(堆)、方法区、Stack(栈)、本地方法栈、程序计数器。其中 Heap 和 方法区 是线程共享的,Stack、本地方法栈 和 程序计数器 是非线程共享的。为什么分为线程共享和非线程共享的呢?请继续往下看。

  首先我们熟悉一下一个 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件(以 .class 为扩展名),每个 Java 程序都需要运行在自己的 JVM 上,被 JVM 通过字节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?

  概括地说,JVM 初始运行的时候都会分配好 Heap 和 方法区,而 JVM 每遇到一个线程,就为其分配一个 Stack、本地方法栈 和 程序计数器。当线程终止时,三者所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与 Java 程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说是发生在 Heap 上)的原因。

1、Heap

Heap(堆)是 JVM 的内存数据区。Heap 的管理很复杂,是被所有线程共享的内存区域,在 JVM 启动的时候创建,专门用来保存对象。在 Heap 中分配一定的内存来保存对象,实际上也只是保存对象的属性值、属性的类型 和 对象本身的类型标记等,并不保存对象的方法(方法以栈帧的形式保存在 Stack 中)。而对象在 Heap 中分配好内存以后,需要在 Stack 中保存一个4个字节的 Heap 内存地址,用来定位该对象在 Heap 中的位置,便于找到该对象。Heap 是垃圾回收的主要场所。Heap 处于物理不连续的内存空间中,只要逻辑上连续即可。

2、方法区

Object Class Data(加载类的类定义数据) 是存储在方法区的。除此之外,常量、静态变量、JIT(即时编译器)编译后的代码也都在方法区。正因为方法区所存储的数据与堆有一种类比关系,所以它还被称为 Non-Heap。方法区也可以是内存不连续的区域组成的,并且可设置为固定大小,也可以设置为可扩展的,这点与堆一样。

垃圾回收在这个区域会比较少出现,这个区域的内存回收主要针对常量池的回收和类的卸载。

3、Stack

先来了解下 Java 指令的构成:

Java 指令由 操作码(方法本身)和 操作数(方法内部变量)组成。   

1)方法本身是指令的操作码部分,保存在 Stack 中;

2)方法内部变量(局部变量)作为指令的操作数部分,跟在指令的操作码之后,保存在 Stack 中(实际上是简单类型(int, byte, short 等)保存在 Stack 中,对象类型在 Stack 中保存地址,在 Heap 中保存值);

  栈也叫栈内存,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就 Over ,所以不存在垃圾回收。也有一些资料翻译成 Java 方法栈,大概是因为它所描述的是 Java 方法执行的内存模型,每个方法执行的同时创建栈帧(Strack Frame)用于存储局部变量表,操作栈(记录出栈、入栈的操作),动态链接,方法出口 等信息,每个方法被调用直到执行完毕的过程,对应着 栈帧 在 栈 的入栈和出栈的过程。

  局部变量表 存放了编译期可知的各种基本数据类型(byte、short、int、long、float、double、boolean、char)、对象的引用( reference 类型,不等同于对象本身)和 returnAdress 类型(指向下一条字节码指令的地址)。局部变量表 所需的内存空间在编译期间完成分配,在方法运行之前,该局部变量表所需要的内存空间是固定的,运行期间也不会改变。

  栈帧是一个内存区块,是一个数据集,是一个有关方法( Method ) 和运行期数据的数据集,当一个方法 A 被调用时就产生了一个栈帧 F1 ,并被压入到栈中,A 方法又调用了 B 方法,于是产生栈帧 F2 也被压入栈,执行完毕后,先弹出 F2 栈帧,再弹出 F1 栈帧,遵循 “先进后出” 原则。

4、本地方法栈

与 Stack 相似,Stack 为 JVM 提供执行 Java 方法的服务,本地方法栈 则为 JVM 提供使用 native 方法的服务。

5、程序计数器

程序计数器是一块较小的内存区域,作用可以看做是当前线程执行的字节码的位置指示器。分支、循环、跳转、异常处理和线程恢复等基础功能都需要依赖这个计数器来完成。

JVM 运行例子

我们来写一个简单的类,代码如下:

public class JVMShowcase {
// 静态类常量
public final static String ClASS_CONST = "I'm a Const"; // 私有实例变量
private int instanceVar = 15; public static void main(String[] args) { // 调用静态方法
runStaticMethod(); // 调用非静态方法
JVMShowcase showcase = new JVMShowcase();
showcase.runNonStaticMethod(100);
} // 常规静态方法
public static String runStaticMethod() {
return ClASS_CONST;
} // 非静态方法
public int runNonStaticMethod(int parameter) {
int methodVar = this.instanceVar * parameter;
return methodVar;
}
}

这个类没有任何意义,不用猜测这个类是做什么用的,只是写一个比较典型的类,然后我们来看看 JVM 是如何运行的,也就是输入 java JVMShowcase 后,我们来看 JVM 是如何处理的:

  • 第 1 步, 向操作系统申请空闲内存。JVM 对操作系统说 “给我 64M(随便模拟数据,并不是真实数据) 空闲内存” ,于是,操作系统就查找自己的内存分配表,找了段 64M 的内存写上 “Java 占用” 标签,然后把内存段的起始地址和终止地址给 JVM, JVM 准备加载类文件。

  • 第 2 步,JVM 分配内存。JVM 获得 64M 内存,就开始得瑟了,首先给 Heap 分配内存,然后给 Stack 也分配好。

  • 第 3 步,文件检查 和 分析 class 文件。若发现有错误即返回错误。

  • 第 4 步,加载类。由于没有指定加载器,JVM 默认使用 bootstrap 加载器,就把 rt.jar 下的所有类都加载,JVMShowcase 也被加载到内存中。我们来看看方法区,如下图:(这时候包含了 main 方法和 runStaticMethod 方法的符号引用,因为它们都是静态方法,在类加载的时候就会加载)

此时,Heap 是空,Stack 是空,因为还没有对象的新建和线程被执行。

  • 第 5 步,执行方法。执行 main 方法。执行启动一个线程,开始执行 main 方法,在 main 执行完毕前,方法区如下图所示:

    (public final static String ClASS_CONST = "I'm a Const"; )

在 方法区 加入了 CLASS_CONST 常量,它是在第一次被访问时产生的(runStaticMethod方法内部)。

Heap 内存中有两个对象 Object 和 showcase 对象,如下图所示:(执行了JVMShowcase showcase = new JVMShowcase(); )

为什么会有 Object 对象呢?因为它是 JVMShowcase 的父类,JVM 是先初始化父类,然后再初始化子类,不管有多少个父类都初始化。

在 Stack 内存中有三个栈帧,如下图所示:

于此同时,还创建了一个程序计数器指向下一条要执行的语句。

  • 第 6 步,释放内存。运行结束,JVM 向操作系统发送消息,说 “内存用完了,我还给你” ,运行结束。

本文永久更新地址:https://github.com/nnngu/LearningNotes/blob/master/JVM/01%20%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3JVM%E7%9A%84%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F.md

01 深入理解JVM的内存区域的更多相关文章

  1. 深入理解jvm之内存区域与内存溢出

    文章目录 1. Java内存区域与内存溢出异常 1.1. 运行时数据区域 1.1.1. 程序计数器 1.1.2. java虚拟机栈 1.1.3. 本地方法栈 1.1.4. Java堆(Java Hea ...

  2. 深入理解 JVM 的内存区域

    深入理解运行时数据区 代码示例: 1. JVM 向操作系统申请内存: JVM 第一步就是通过配置参数或者默认配置参数向操作系统申请内存空间,根据内存大小找到具体的内存分配表,然后把内存段的起始地址和终 ...

  3. 深入理解JVM - Java内存区域与内存溢出异常 - 第二章

    一 运行时数据区域 JVM在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间. 程序计数器 程序计数器(Program Counter ...

  4. JVM的内存区域划分

            JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的 ...

  5. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  6. JVM的内存区域划分(转)

    原文链接:JVM的内存区域划分 JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内 ...

  7. 【java】JVM的内存区域划分

    学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的呢? 由于Java程序是交由JVM执行 ...

  8. JVM(二)-内存区域之线程私有区

    概述: 对于从事C.C++开发的程序员来说,在内存管理领域,他们既是拥有最高权力的"皇帝",又是从事最基础工作的劳动人民--既拥有每个对象的"所有权", 又担负 ...

  9. JVM的内存区域模型

    首先要明白一个概念,就是JVM的内存区域划分与java的内存区域模型是两个不同的概念,前者指的是在java中jvm会将一个程序划分为哪些块来存储对应的数据,后者是一个更宏观上的j概念,指的是java线 ...

随机推荐

  1. Mysql 范围查询优化

    Range查询:用单独的Index的一个或多个index值来检索表的子集行数据,当然包含多个index. 1:一个index (单一部分)的range access 方法:(eg : 指的这种key ...

  2. python简单词频统计

    任务 简单统计一个小说中哪些个汉字出现的频率最高 知识点 文件操作 字典 排序 lambda 代码 import codecs import matplotlib.pyplot as plt from ...

  3. excel生成sql语句

    很多时候,我们想把excel中数据导入到数据库中. 方法有很多种,比如直接拷贝然后黏贴进编辑状态的框中,这种情况有个弊端,就是excel中每列的数据必须和数据库中一一对应,这个很难,基本上不太可能,数 ...

  4. IOS 使用 ZbarSDK 二维码扫描

    1. 下载SDK   https://github.com/bmorton/ZBarSDK 2. 引用到项目中 3. 添加引用 4. AppDelegate中添加下面代码 5. 在需要使用扫描的con ...

  5. FORTH基本堆栈操作

    body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...

  6. tomcat三种启动不同的启动方式

    Linux下tomcat服务的启动.关闭与错误跟踪,通常通过以下几种方式启动关闭tomcat服务: 切换到tomcat主目录下的bin目录 1. 启动tomcat服务 方式一:直接启动 ./start ...

  7. Hystrix-命令名称、分组以及线程池划分

    命令名称: 1.1 以继承的方式实现Hystrix命令使用类名作为默认的命令名称,我们也可以在构造函数中通过Setter静态类来设置 1.2 为了保存每个命令分配的Setter分配,你也可以像这样缓存 ...

  8. 04_Javascript初步第一天

    视频来源:麦子学院[看来是麦子的忠实粉丝] 讲师:King我只是想记住对应的中文翻译:Uncaught SyntaxError: missing ) after argument list//属性列表 ...

  9. PYTHON3 中的虚假四舍五入:round()

    PYTHON3 中的虚假四舍五入:round() 创建时间: 2017/12/5 17:08 作者: CN_Simo 标签: python基础, round, 四舍五入 一.这不是一个BUG! 在使用 ...

  10. [DeeplearningAI笔记]神经网络与深度学习2.1-2.4神经网络基础

    觉得有用的话,欢迎一起讨论相互学习~Follow Me 2.1 二分分类 在二分分类问题中,目标是训练出一个分类器,它以图片的特征向量x作为输入,预测输出的结果标签y是1还是0.在图像识别猫图片的例子 ...