• 【JVM】内存布局(又叫Java运行时数据区)


    • JVM 堆中的数据是共享的,是占用内存最大的一块区域。
    • 可以执行字节码的模块叫作执行引擎。
    • 执行引擎在线程切换时怎么恢复?依靠的就是程序计数器。
    • JVM 的内存划分与多线程是息息相关的。像我们程序中运行时用到的栈,以及本地方法栈,它们的维度都是线程。
    • 本地内存包含元数据区和一些直接内存。
    • 本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是 native 方法。你甚至可以认为虚拟机栈和本地方法栈是同一个区域,这并不影响我们对 JVM 的了解

    Java虚拟机栈(JVM Stacks)

    Java 虚拟机栈是基于线程的,每一个线程都有一个栈,哪怕你只有一个 main() 方法,也是以线程的方式运行的。在线程的生命周期中,参与计算的数据会频繁地入栈和出栈,栈的生命周期是和线程一样的。
    栈里的每条数据,就是栈帧。在每个 Java 方法被调用的时候,都会创建一个栈帧,并入栈。一旦完成相应的调用,则出栈。所有的栈帧都出栈后,线程也就结束了。每个栈帧,都包含四个区域:

    局部变量表

    操作数栈

    动态连接

    返回地址(return address)

    因为这种类型只存在于字节码层面,所以我们平常打交道的比较少。对于 JVM 来说,程序就是存储在方法区的字节码指令,而returnAddress 类型的值就是指向特定指令内存地址的指针。

    程序计数器(Program Counter)

    每一个线程都有他自己的程序计数器
    存放指令位置,虚拟机的运行类似于这样的循环:

    while(not end){
        //取PC中的位置,找到对应位置的指令;
        //执行该指令;
        //PC++;
    }
    

    每一个线程都有一个Program Counter,程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。这里面存的,就是当前线程执行的进度,下面这张图,能够加深对这个过程的理解

    程序计数器还存储了当前正在运行的流程,包括正在执行的指令、跳转、分支、循环、异常处理等。
    下面这张图,就是使用 javap 命令输出的字节码。大家可以看到在每个 opcode 前面,都有一个序号。就是图中红框中的偏移地址,可以认为它们是程序计数器的内容。

    堆 (Heap)


    • 堆是 JVM 上最大的内存区域,我们申请的几乎所有的对象,都是在这里存储的。我们常说的垃圾回收,操作的对象就是堆
    • 堆空间一般是程序启动时,就申请了,但是并不一定会全部使用
    • 随着对象的频繁创建,堆空间占用的越来越多,就需要不定期的对不再使用的对象进行回收。这个在 Java 中,就叫作 GC(Garbage Collection)

    2.创建对象的时候,到底是在堆上分配,还是在栈上分配呢?

    这和两个方面有关:对象的类型和在Java 类中存在的位置。
    普通对象:JVM如果开启了逃逸分析,满足在栈上分配的条件,就会在栈上分配,如果栈上分配不下,才在堆上创建对象,然后在其他地方使用的其实是它的引用。比如,把这个引用保存在虚拟机栈的局部变量表中。
    基本数据类型:每个线程拥有一个虚拟机栈。在方法体内声明了基本数据类型的对象,它就会在栈上直接分配

    直接内存

    JVM 可以直接访问内核空间的内存(OS管理的内存)
    NIO,提高效率,实现零拷贝(Zero Copy)

    元空间(Metaspace)

    再来看一下这个JVM内存区域划分图

    为什么有 Metaspace 区域?它有什么问题?

    在 Java 8 之前,生成的 X.class这些类的元数据信息是放在堆上一个叫 Perm 区的内存里面的。更早版本,甚至 String.intern 相关的运行时常量池也放在这里。原来的 Perm 区(又称为永久代)是在堆上的,这个区域有大小限制,而且不会垃圾回收,因此很容易造成 JVM 内存溢出,从而造成 JVM 崩溃。
    Perm 区在 Java 8 中已经被彻底废除,取而代之的是 Metaspace。现在的元空间是在非堆上的,这是背景。看下图:

    元空间的好处也是它的坏处

    好处:使用非堆可以使用操作系统的内存,JVM 不会再出现方法区的内存溢出;
    坏处:无限制的使用会造成操作系统的死亡。所以,一般也会使用参数 -XX:MaxMetaspaceSize 来控制大小,如果不设置仅仅会受限于物理内存的大小

    方法区(Method Area)

    1.8版本之前字符串位于Perm Space。FullGC不会清理
    1.8版本之后,字符串常量位于堆,会触发FullGC

    我们常说的字符串常量,存放在哪呢?

    由于常量池,1.7之前是在Perm永久代里,从1.8开始放到了堆中,我们创建的字符串,将会在堆上分配。

  • 相关阅读:
    Ubuntu 制作run安装包 依赖mono开发的软件 半自动安装包
    Windows C# to Linux Mono
    Ubuntu 18.04 操作简记
    Ubuntu 安装 Qt 简记
    Visual Studio操作记录
    WinForm项目中使用Xaml资源字典
    加载大量控件
    executable file and DLL
    微信小程序IOS系统兼容 Date.parse() 时间字符串转时间戳
    谷歌浏览器禁用 页面js
  • 原文地址:https://www.cnblogs.com/zhangyibing/p/13754968.html
Copyright © 2020-2023  润新知