前言

  最近在准备面试题刷到了JVM这块,作为一个小白,巩固知识点最好的方式就是亲手写出来并分享;相信我的理解,同样是小白的你,一定有很大的帮助。不信,请你往下看!

JVM内存区域简介

  如果有人问Java的内存区域或者运行时数据区域,说的就是JVM内存区域

  Java程序在运行的时候,Java虚拟机所管理的内存是被划分为若干个数据区域,注意这些数据区域不是固定死的,抽象得可以分成为JDK1.8前后的JVM内存区域,但是总体上差别不大。

一.JDK1.8前的JVM内存区域

  JVM内存区域从线程的角度可以分成:线程共享和线程私有;

    》线程共享的区域有:堆,方法区(永久代),直接内存(非运行时数据区域)

    》线程私有的区域有:程序计数器,虚拟机栈,本地方法栈

  如果有人问JVM内存区域,一般讲这5个就行:程序计数器,虚拟机栈,本地方法栈,堆,方法区;注意直接内存并不是运行时数据区域的一部分

1.程序计算器

  程序计算器是一块比较小的内存区域。大家应该知道线程在轮流切换执行时,线程执行到哪,那下一次拿到CPU使用权执行任务就从哪开始继续执行,简言之:从哪跌倒从哪爬起来;那是不是我们得知道它从哪跌倒的啊?没错,程序计算器的作用就在这了,你可以把它理解成是当前线程所执行的指令(字节码)的行号指示器,保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址)。

  线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,生命周期和线程一致。

  如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值则为空(Undefined)。由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

2.虚拟机栈(Java栈)
  虚拟机栈描述的是 Java 方法执行的内存模型。什么意思呢?大家看培训班的视频的时候,机构老师应该会有提过一个术语:方法进栈。没错,这里的栈就是虚拟机栈,在执行java的方法的同时,会对应创建一个栈帧,方法进栈的“方法”明确的讲就是栈帧,虚拟机栈的组成部分也是栈帧;
  当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。看下图:

  栈帧可以理解为一种保存数据的基本数据结构,你只需要关心它保存的是什么即可,在上面的图说的很清楚了:局部变量表,操作栈等等

    》局部变量表:局部变量表是存放方法参数和局部变量的区域;对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译期就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。

    》操作栈:是个初始状态为空的桶式结构栈。在方法执行过程中, 会有各种指令(语句)往栈中写入和提取信息。JVM 的执行引擎是基于栈的执行引擎, 其中的栈指的就是操作栈。字节码指令集的定义都是基于栈类型的,栈的深度在方法元信息的 stack 属性中。

    》动态链接:指向当前方法所属的类的运行时常量池的引用;这个不是必须有的,如果方法中有使用到方法所属类中的常量,那这个动态链接就是这个常量在运行时常量池中的引用

    》方法返回地址:当一个方法执行完毕之后,要返回到之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

3.本地方法栈

  很多人对本地方法栈和java栈给搞混了,由于java栈是为java方法所服务的,因此也被叫做方法栈,那混淆就来了;

  方法栈是虚拟机栈,不是本地方法栈!

  方法栈是虚拟机栈,不是本地方法栈!

  方法栈是虚拟机栈,不是本地方法栈!重要事情说三遍!!

  ok,回到本地方法栈。本地方法栈其实和Java栈的作用和原理是很相似的。最大的区别在于Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。

4.Java堆

  对于大多数Javac程序来说,Java 堆是 Java 虚拟机所管理的内存中最大的一块,也是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

  堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。从GC的角度来看, 堆中还可以细分为:新生代和老年代;新生代再细致一点还可以分为: Eden 空间、From Survivor 空间、To Survivor 空间。

  Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,当前主流的虚拟机都是按照可扩展来实现的(通过 -Xmx 和 -Xms 控制)。如果在堆中没有足够的内存区完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。

  来!我们顺便在拓展下新生代和老年代的内容,这可是面试的热点,先上图:

  我们说过从GC的角度,堆内存还分成老年代和新生代。

  新生代:是用来存放新生或者创建不久的对象。一般占据堆的 1/3 空间。如果频繁创建对象,那么新生代会频繁地触发GC进行垃圾回收,因此新生代又分为 Eden 区、 ServivorFrom、 ServivorTo 三个区。

    1.Eden 区:Java 新对象的出生点(如果新创建的对象占用内存很大,则直接分配到老年代),当 Eden 区内存不够的时候就会触发 GC,对新生代区进行一次垃圾回收。

    2.ServivorFrom:上一次 GC 的幸存对象,但是它会作为本次 GC 的扫描目标

    3.ServivorTo:保留了一次 GC 过程中的幸存对象,简单说就是GC未清除对象会被放在这个区域。

  GC在回收新生代的过程有3个阶段:复制-->清空-->互换

    复制:首先,eden、servicorFrom区域存活的对象会先被复制到 ServicorTo,同时这些对象的年龄+1(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),如果出现 复制的过程中ServicorTo 不够内存了,对象就放到老年区。

    清空:然后,清空 Eden 和 ServicorFrom 中的对象;

    互换:最后, ServicorTo 和 ServicorFrom的对象进行互换,这样的话,原 ServicorTo 将成为下一次 GC要清除的ServicorFrom区

  老年代:主要存放应用程序中生命周期长的内存对象,说简单点,就是很多次逃过GC的回收或者是因为对象内存过大的对象会被放到这里;老年代的对象比较稳定,所以GC 不会频繁执行。在进行 GC 前一般都是先进行了一次GC,使得有一些新生代的对象晋身入老年代,导致空间不够用时才触发GC。当没有足够大的连续空间分配给新创建的较大对象时也会提前触发一次 GC 进行垃圾回收腾出空间。

5.方法区

  方法区(Method Area)也是各个线程共享的内存区域,字节码文件被类加载器加载到JVM的第一块区域就是我们的方法区,它用于存储类信息、常量、静态变量等。

  Java 虚拟机规范对方法区的限制非常宽松,除了和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。垃圾收集行为在这个区域是比较少出现的,其内存回收目标主要是针对常量池的回收和对类型的卸载。当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。

  方法区还有一个特别重要的区域就是运行时常量池,我们要知道Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于保存编译期生成的各种字面量和符号引用;在类加载并进入方法区后,常量池的内容会被放到运行时常量池中存放。

  运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性,Java 语言并不要求常量一定只有编译期才能产生,也就是说并不是先放到Class 文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是 String 类的 intern() 方法。既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

  最后,相信大家经常听到方法区和永久代,元空间等来挂钩甚至等同起来,如果要比喻他们的关系,方法区相当接口,永久代和元空间相当实现类,在JDK1.8前,HotSpot虚拟机(HotSpot是Java虚拟机的实现)以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。

6.直接内存

  直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域,但它会被频繁得使用;可以简单得理解为JVM的外设内存。

二.JDK1.8的JVM内存区域

    》线程共享的区域有:堆,直接内存(非运行时数据区域),元空间

    》线程私有的区域有:程序计数器,虚拟机栈,本地方法栈

  到JDK1.8其实和之前差别不大,唯一的不同在于JDK1.8 前,Hotspot 中方法区的实现是永久代,使用的是堆内存来保存对象实例;JDK1.8 开始使用元空间,以前永久代的所有字符串常量由堆内存进行管理,其他内容被放到了元空间,元空间直接在本地内存分配。

  以上是个人的一些理解,如果大家觉得哪里不妥,欢迎指正!

参考资料:

  https://www.cnblogs.com/dolphin0520/p/3613043.html

https://github.com/Snailclimb/JavaGuide/blob/3965c02cc0f294b0bd3580df4868d5e396959e2e/Java%E7%9B%B8%E5%85%B3/%E5%8F%AF%E8%83%BD%E6%98%AF%E6%8A%8AJava%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E8%AE%B2%E7%9A%84%E6%9C%80%E6%B8%85%E6%A5%9A%E7%9A%84%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0.md

  https://www.cnblogs.com/czwbig/p/11127124.html

小白也能看懂的JVM内存区域的更多相关文章

  1. 小白也能看懂的Redis教学基础篇——做一个时间窗限流就是这么简单

    不知道ZSet(有序集合)的看官们,可以翻阅我的上一篇文章: 小白也能看懂的REDIS教学基础篇--朋友面试被SKIPLIST跳跃表拦住了 书接上回,话说我朋友小A童鞋,终于面世通过加入了一家公司.这 ...

  2. 小白也能看懂的插件化DroidPlugin原理(二)-- 反射机制和Hook入门

    前言:在上一篇博文<小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理>中详细介绍了 DroidPlugin 原理中涉及到的动态代理模式,看完上篇博文后你就会发现原来动态代 ...

  3. 小白也能看懂的插件化DroidPlugin原理(三)-- 如何拦截startActivity方法

    前言:在前两篇文章中分别介绍了动态代理.反射机制和Hook机制,如果对这些还不太了解的童鞋建议先去参考一下前两篇文章.经过了前面两篇文章的铺垫,终于可以玩点真刀实弹的了,本篇将会通过 Hook 掉 s ...

  4. 小白也能看懂的Redis教学基础篇——朋友面试被Skiplist跳跃表拦住了

    各位看官大大们,双节快乐 !!! 这是本系列博客的第二篇,主要讲的是Redis基础数据结构中ZSet(有序集合)底层实现之一的Skiplist跳跃表. 不知道那些是Redis基础数据结构的看官们,可以 ...

  5. 【vscode高级玩家】Visual Studio Code❤️安装教程(最新版🎉教程小白也能看懂!)

    目录 如果您在浏览过程中发现文章内容有误,请点此链接查看该文章的完整纯净版 下载 Linux Mac OS 安装 运行安装程序 同意使用协议 选择附加任务 准备安装 开始安装 安装完成 如果您在浏览过 ...

  6. 搭建分布式事务组件 seata 的Server 端和Client 端详解(小白都能看懂)

    一,server 端的存储模式为:Server 端 存 储 模 式 (store-mode) 支 持 三 种 : file: ( 默 认 ) 单 机 模 式 , 全 局 事 务 会 话 信 息 内 存 ...

  7. 初始jvm(一)---jvm内存区域与溢出

    jvm内存区域与溢出 为什么学习jvm 木板原理,最短的一块板决定一个水的深度,当一个系统垃圾收集成为瓶颈的时候,那么就需要你对jvm的了解掌握. 当一个系统出现内存溢出,内存泄露的时候,因为你懂jv ...

  8. JVM内存区域划分及垃圾回收

    第一部分.闲扯+概述 近来在研读<深入理解java虚拟机>一书,读完之后做个小结,算是记录一下自己的学习所得,在成长的路上,只能死磕. 要理解JVM,就要先从其内存区域划分开始,知道其由几 ...

  9. JVM 内存区域 (运行时数据区域)

    JVM 内存区域 (运行时数据区域) 链接:https://www.jianshu.com/p/ec479baf4d06 运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内 ...

随机推荐

  1. ECMAScript 6新特性简介

    目录 简介 ECMAScript和JavaScript的关系 let和const 解构赋值 数组的扩展 函数的扩展 简介 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言 ...

  2. MyBatis学习(二)代码实战

    一.项目依赖 本项目是基于mybatis3.4.6版本实现的,用到的jar包如下 二.项目结构解析 三.配置文件解析 四.mapper文件解析 <?xml version="1.0&q ...

  3. Oracle添加键值对盲注

    前言 遇到一种注入点,存在于POST参数中,却不能用sqlmap扫出: 分析 request参数格式: %24Q_value1=test1&orderCol=&order=+ASC+& ...

  4. 记一次GDB调试

    目标文件: ciscn_2019_ne_5. 来源 :https://buuoj.cn/challenges 保护情况:保护是没有保护的 主要伪代码: int __cdecl main(int arg ...

  5. Python-序列切片原理和切片协议-[start:end:step] __getitem__

    切片原理图(顾头不顾尾的正则原理) # [0:1] 其实只取到C, 取e则 [-1:], 如果步长为负数则倒过来取,从第几个往回取 name = "ChuiXue" print(n ...

  6. Centos-关机重启

    为何要使用命令进行关机重启? linux系统中的各个进程携带着各种数据,强制关机会照成数据混乱而丢失数据,甚至可能损坏硬件,所以我们需要更加安全的关机和重启方式 关机重启相关命令,需要root用户才能 ...

  7. C语言中 malloc

    参考:https://blog.csdn.net/kokodudu/article/details/11760863 一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: ...

  8. (转)DBC文件格式解析

    Dbc是描述CAN通信报文和信号信息的文件,用Vector Candb++打开. 用记事本打开后,可以看到固定格式,下面的博客做了详细的解析: https://blog.csdn.net/weixin ...

  9. Linux桌面环境配置

    目录 更换软件源 中文输入法 firefox安装flash插件 编译安装Vim 关闭蓝牙开机自启 yakuake无法正常使用 在中文环境下将默认目录修改成英文 电脑换成了thinkpad x1c 20 ...

  10. dict, hash

    dict: dictKey -- > dictVal example: dictEntry *dictFind(dict *d, const void *key)     Key is like ...