栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

从零开始JVM 01 : 内存结构

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

从零开始JVM 01 : 内存结构


  “他强由他强,清风拂山岗;他横由他横,明月照大江。他自狠来他自恶,我自一口真气足。” ——《倚天屠龙记》
  毕竟是自己的博客,不希望只是冷冰冰的知识,所以就把我最喜欢的一句话作为序。

  就以一张经典的JVM内存结构图作为开头,咱们开始!

  不用纠结细节,这张图也只是一个引子。接下来的一段时间里,我也会按照:JVM内存结构,垃圾回收,字节码及类加载前的优化,类加载,解释器和编译器的优化的顺序,陆续更新完从零开始系列,也是对我自己学习路程的一个回顾。

 JVM内存结构
    • 一、程序计数器
      • 1.1 定义
      • 1.2 作用
      • 1.3 特点
    • 二、虚拟机栈
      • 2.1 定义
      • 2.2 作用
      • 2.3 特点
    • 三、本地方法栈
      • 3.1定义
    • 四、堆
      • 4.1 定义
      • 4.2 特点
    • 五、方法区
      • 5.1 定义
      • 5.2 具体实现

一、程序计数器 1.1 定义

  程序计数器是一个记录着当前线程所执行的字节码的行号指示器,它记的是下一条jvm指令的执行地址。它是java对硬件的抽象,在物理上是由寄存器实现的。

1.2 作用

  在说明程序计数器的作用之前,我们先来看看这段简单的java代码。

public class PrintNum {
    public static void main(String[] args) {
        System.out.println(1);
        System.out.println(2);
        System.out.println(3);
    }
}

  下面是该源代码被编译成JVM指令的结果。

  对于现在的我们,无需理解每条指令的具体作用。观察这张图,不难发现每个指令前都有一个数字,这是指令的偏移地址,我们简称为行号,程序计数器的作用就是存储下一条指令的行号。
  以上图为例,当第一条指令执行时,JVM会将下一条指令的行号“3”放入程序计数器中。当第一条指令执行完时,解释器会根据程序计数器中的行号拿到下一条指令,继续运行。

  有同学可能要问了,为什么要多此一举先把下一条指令的行号放入程序计数器中,直接取指令然后执行不是更简单吗?
  这其实和多线程有关。JVM是支持多个线程同时运行的,这就涉及到CPU的调度问题了。线程甲正执行的好好的, 大哥CPU告诉甲说,你累了,我陪会儿乙,甲只好乖乖休息。一段时间后,大哥回来了,这时甲就可以根据程序计数器中的行号取到下一条指令接着执行。
  这里有一点要注意,因为程序计数器记录的是行号,是会重复的,所以多个线程不能同时用一个,不然就乱了。所以程序计数器是线程私有的。

1.3 特点
  • 线程私有。这点上面已经解释过了。
  • 不会存在内存溢出。程序计数器中的行号永远只会有一个,当前指令执行时,会拿下一条指令的行号替换当前的行号。因此就不存在内存溢出问题。
二、虚拟机栈 2.1 定义

  栈:虚拟机栈是每个线程运行所需要的内存。
  栈帧:每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存。

2.2 作用

  虚拟机栈是用于描述java方法执行的内存模型。每一个方法执行时,会创建一个栈帧,栈帧的结构为:局部变量表、操作数栈、动态链接、方法出口,可以理解成一种数据结构,专门用于描述java方法执行的数据结构。虚拟机栈的最小单位就是栈帧。
  线程运行中,当执行到一个方法时,就会生成一个栈帧并压入栈中。当这个方法执行完之后,会将这个栈帧弹出,释放对应的内存空间。
  一个栈中可能同时存在多个栈帧,如方法一调用方法二时,就会将两个栈帧都压入栈中。
​   注意每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。

简单理解:如当执行main方法时,就会开启一个main线程。这时虚拟机就要拿出一块区域给这个线程使用,这一块区域就是虚拟机栈。同时也不难理解,虚拟机栈也和程序计数器是线程私有的。
  java的线程的运行其实就是在执行一个个方法,JVM通过栈帧来表示方法的执行。于是当线程中有方法执行时,就需要对应生成一个栈帧放入虚拟机栈中。表示此线程有此方法正在运行。当该方法执行完之后,这个栈帧也会弹栈,释放其内存,所以也不涉及垃圾回收。

2.3 特点
  • 不涉及垃圾回收。因为当方法执行完,栈帧会弹栈,对应的内存就会释放掉。不会产生垃圾。
  • 注意栈的大小,当分配给栈的内存过大时,相应地,同时可运行地线程数就会减少。
三、本地方法栈 3.1定义

本地方法:java语言是有限制的,不能直接和操作系统打交道,因此就需要通过调用c,c++编写的方法来和底层交互。这些用c,c++编写地方法就是本地方法。(代码中用native修饰的方法就是本地方法。)
本地方法栈:本地方法执行用到的内存空间就是本地方法栈。

四、堆 4.1 定义

  堆是我们重点需要了解的区域,也是垃圾回收的重点关注对象。我们将在之后详细讲解。
  当前,我们只需要知道:通过new关键字、创建对象都会使用到堆内存即可。

4.2 特点
  • 存在内存溢出。如果新建的对象一直被回收就会造成内存溢出问题。
  • 是线程共享的。所以堆中对象都要考虑线程安全问题。
五、方法区 5.1 定义

  方法区是JVM所有线程共享的区域,它存储了与类结构相关的信息:运行时常量池,成员变量,方法数据,成员方法、构造方法的代码。
  方法区在虚拟机启动时创建,它在逻辑上是堆的组成部分(具体实现上是否为堆的一部分视厂商而定,如永久代,元空间都是其实现)。JVM规范并不强制方法区的位置。

5.2 具体实现

  在1.6中方法区的实现叫永久代,1.8叫元空间。二者区别在于StringTable的位置不同。并且在1.8中,方法区被移出JVM内存,被放置在本地内存(操作系统内存)中。


  从这两张图中,我们能发现一个特别的区域StringTable,这也是一个很重要的区域,我们之后再聊。

  这次我们只是简单介绍了JVM内存结构中的几个大哥。下一次,我们会介绍几种jdk自带的工具,同时也会详细介绍StringTable这块区域。
  从零开始,我们一起学JVM!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/318713.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号