Java虚拟机的内存空间分为五个部分:

  • 方法区
  • 虚拟机栈
  • 本地方法栈
  • 程序计数器

程序计数器

定义

程序计数器是一个块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指令器。程序计数器记录的是当前线程正在执行的那一条字节指令的地址。

如果当前线程正在执行的是一个本地方法,那么程序计数器为空。

作用

程序计数器有两个作用:

  1. 字节码解释器可以通过改变程序计数器的值,从而实现堆代码执行顺序的控制,如顺序、循环;
  2. 可以线程切换的情景下,记录当前线程的运行位置。

特点

  1. 占用空间小;
  2. 线程私有;
  3. 唯一一个不会出现OutOfMemoryError的内存区域。
  4. 生命周期伴随线程的生死。

Java虚拟机栈

定义

Java虚拟机栈是描述Java方法执行线程内存模型。
在方法被执行的时候,Java虚拟机栈会同步创建一个栈帧用于存储方法运行时所需的信息,包括:

  • 局部变量表
  • 操作数栈
  • 动态连接
  • 方法出口等

特点

  1. 每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
  2. 经常有人把Java内存区域笼统地分为堆内存和栈内存,这样划分过于粗糙。栈通常指地就是Java虚拟机栈,或者更多情况下指的是虚拟机栈中局部变量表部分。
  3. 局部变量表存放了编译器客之的各种Java虚拟机基本数据类型、对象引用和returnAddress类型(指向了条字节码指令的地址)。
    这些数据类型在局部变量表中的存储空间以局部变量槽Slot在表示,其中64位长度的数据类型会占用两个变量槽,其余只占用一个。所以,一个变量槽的大小是32位的。局部变量表所需的内存空间在编译期间完成分配,进入方法是时,这一个方法子啊栈帧中分配的局部变量大小不会改变。
  4. 虚拟机栈会抛出两种异常:
    • 如果线程请求的栈深度大于虚拟机所允许的深度,就会抛出StackOverflowError异常;
    • 如果虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
  5. 线程私有。

注:StackOverFlowError和OutOfMemoryError的异同?
StackOverFlowError表示当前线程申请的栈超过了事先定好的栈的最大深度,但内存空间可能还有很多。
而OutOfMemoryError是指当线程申请栈时发现栈已经满了,而且内存也全都用光了。

本地方法栈

定义

本地方法栈为虚拟机使用的本地方法服务,功能与虚拟机栈类似。

本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。

Java堆

定义

堆是用来存放对象实例的空间,还包括数组、字符串。
几乎所有对象实例都存放在堆中。现值类型的支持,通过即时编译技术,对象可能会分配到其他内存中。

特点

  1. 是各个线程共享的内存区域。(整个Java虚拟机只有一个堆,所有的线程都访问同一个堆。而程序计数器、Java虚拟机栈、本地方法栈都是一个线程对应一个的。)
  2. 在虚拟机启动时候创建。
  3. 垃圾回收的主要场所。
  4. 可以细分为:新生代和老年代。新生带可以分为Eden、From Survior、To Survior。所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区,用于提升分配时的效率。将堆细分知识为了更好地回收内存,或者更快地分配内存。
  5. 堆的在逻辑内存空间应当是连续的。
  6. 堆的大小可以是固定的,也可以是可扩展的。主流的虚拟机是按照可扩展的来实现的(通过参数-Xmx和-Xms设定)。如果堆中内存大小无法满足实例的分配,而且也无法扩展时,虚拟机将会抛出OutOfMemoryError异常。

方法区

定义

方法区是用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
《Java虚拟机规范》 中把方法区描述为堆的一个逻辑部分,从逻辑上可以与堆区分开。

特点

  1. 线程共享:是堆的逻辑部分,因此和堆一样是线程共享的。虚拟机中方法区唯一。
  2. 永久代:方法区的数据与需要长期存在,它是堆的逻辑部分,所以,用堆的划分方法,可以把方法区称为老年代。JDK8,HotSpot用元空间取代了永久代。
  3. 内存回收效率低,因为其中数据需要长期存在,所以一次回收只能回收少量数据。
    对方法区的内存回收目标是对常量池的回收和对类型的卸载。
  4. 虚拟机规范对方法去要求比较宽松,可以不实现垃圾回收。

运行常量池

运行常量池用于存储方法区中常量的这一部分。

在一个类中通过public static final来声明一个常量。这个类被编译后便生成Class文件,这个类的所有信息都存储在这个class文件中。

运行常量池相对于Class文件常量池具有动态性,在运行期间也可以将新的常量放入池中,例如String类的intern()方法。

当常量池无法申请到内存时会抛出OutOfMemoryError异常。

常量池中的某些常量没有被引用时就需要被回收。

直接内存

直接内存并不属于Java虚拟机的内存,但是也会被频繁地使用。

在NIO类,引入了一种基于通道与缓冲区的IO方式。它可以Native函数库直接分配堆外内存,然后通过一个存储在Java堆里的DirectByteBuffer对像作为这块内存的引用进行操作。避免了Java堆和Native堆之间的数据复制,从而提高了内存效率。

直接内存的分配不会收到Java虚拟机堆大小的限制,但是,既然是内存,还是会收到本机总内存的限制,不够的时候就会抛出OOM。

总结

  1. 线程私有的区域:程序计数器、虚拟机栈、本地方法栈。生命周期和其所属线程一样。
  2. 线程共享的区域:堆和方法区。虚拟机启动就创建,停止就销毁。
  3. 只有程序计数器不会抛出OOM异常,其他内存区域如果无法向虚拟机申请到最后的空间,都会抛出OOM。
  4. Java虚拟机内存模型中的两个栈分别是虚拟机栈和本地方法栈。两个栈的区别是:虚拟机栈描述的是Java方法运行过程的内存模型,而本地方法栈式描述Java本地方法过程的内存模型。
  5. Java虚拟内存模型中的两个堆分别是Java堆和方法区。方法区属于Java堆的逻辑部分。区别是:堆中存放实例对象,而方法区存放类信息、常量、静态变量、即时编译器编译的代码数据。
  6. 堆是Java虚拟机中最大的一块内存区域,也是垃圾收集器的主要工作区域。

深入理解Java虚拟机(一)——JVM内存模型的更多相关文章

  1. 【java虚拟机】jvm内存模型

    作者:pengjunlee原文链接:https://blog.csdn.net/pengjunlee/article/details/71909239 目录 一.运行时数据区域 1.程序计数器 2.J ...

  2. 深入理解Java虚拟机之JVM内存布局篇

    内存布局**** ​ JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...

  3. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  4. 《深入理解Java虚拟机:JVM高级特性与最佳实践》【PDF】下载

    <深入理解Java虚拟机:JVM高级特性与最佳实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062566 内容简介 作为一位 ...

  5. 读书笔记-《深入理解Java虚拟机:JVM高级特性与最佳实践》

    目录 概述 第一章: 走进Java 第二章: Java内存区域与内存溢出异常 第三章: 垃圾收集器与内存分配策略 第四章: 虚拟机性能监控与故障处理 第五章: 调优案例分析与实战 第六章: 类文件结构 ...

  6. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  7. 深入理解Java虚拟机(自动内存管理机制)

    文章首发于公众号:BaronTalk 书籍真的是常读常新,古人说「书读百遍其义自见」还是很有道理的.周志明老师的这本<深入理解 Java 虚拟机>我细读了不下三遍,每一次阅读都有新的收获, ...

  8. 《深入理解 java 虚拟机》学习 -- 内存分配

    <深入理解 java 虚拟机>学习 -- 内存分配 1. Minor GC 和 Full GC 区别 概念: 新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java ...

  9. 《深入理解Java虚拟机:JVM高级属性与最佳实践》读书笔记(更新中)

    第一章:走进Java 概述 Java技术体系 Java发展史 Java虚拟机发展史 1996年 JDK1.0,出现Sun Classic VM HotSpot VM, 它是 Sun JDK 和 Ope ...

随机推荐

  1. shell简介及变量的定义查看撤销

    1.shell分类及相关软件  图形界面Shell(Graphical User Interface shell 即 GUI shell),如:GNOME.KDE 命令行式Shell(Command ...

  2. 分布式监控系统之Zabbix宏、模板和自定义item

    前文我们聊了下zabbix的基础使用,包括主机的添加.监控项.触发器.action以及告警通知的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/140073 ...

  3. iczer的vue-antd-admin项目,逐步平滑迁移mock的url

    这个需求,在实战中蛮有用的.但没有看到网上太多文档,就自己hack了一个思路.供指正. 需求 在前后端分离的项目开发中,前后端的开发步骤和进度是不一致的.有时,前端为了不等待后端的API开发进度,会自 ...

  4. Dockerfile中如何自动回答标准输入的问题

    前言大家在用docker build制作自己的image的時候,都会用RUN命令来执行一些操作来安装某些必须的软件. 而一些软件的安装过程中会需要用户来输入yes/no或者y/n来确定一些东西后才能进 ...

  5. JS逆向课程笔记

    扩展知识 Sources-js代码格式化

  6. MathType单边大括号的编辑技巧你知道吗?

    大家都知道,一般情况下,数学里面的括号都是成对出现的,但是也有些情况下可以只用到单边的括号,就比如分段函数,在编写的时候只需用到左半边的括号.MathType作为专业的公式编辑器,用它来编写公式再方便 ...

  7. 【PYTEST】第四章Fixture

    知识点: 利用fixture共享数据 conftest.py共享fixture 使用多个fixture fixture作用范围 usefixture 重命名 1. 利用fixture共享数据 test ...

  8. Java线程的死锁和活锁

    目录 1.概览 2.死锁 2.1.什么是死锁 2.2 死锁举例 2.3 避免死锁 3.活锁 3.1 什么是活锁 3.2 活锁举例 3.3 避免活锁 1.概览 当多线程帮助我们提高应用性能的同时,它同时 ...

  9. 关于CopyOnWriterArrayList的一些理解

    学了cowarraylist之后,有些不明白的地方, 1.我们为什么要用写时复制的策略呢?,这样每次不是都要复制吗,性能不是很低吗?直接在元素组上扩容不好吗?而且读的时候数据一致性也保证不了,如果只是 ...

  10. Linux中redis服务开启

    集群模式设置为no 就可以开启服务 cluster-enable no