当前位置: 首页 > news >正文

用几张图深度剖析Java运行时数据区

(一)基础概念介绍

首先看一张图:下图是Java虚拟机运行时数据区,JVM的内存模型可以分为方法区、虚拟机栈、本地方法栈、堆和程序计数器。

运行时数据区

首先还是介绍一下基本概念

程序计数器

程序计数器的作用可以看成是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变计数器的值来选择下一条需要执行的字节码的指令。java虚拟机的多线程是通过线程轮流切换来分配处理器执行时间的方式实现的,为了线程切换之后能恢复到正确的执行位置,每个线程就需要一个独立的程序计数器。

java虚拟机栈
java虚拟机栈线程私有,每个方法被执行的同时都会创建一个栈帧用于存放局部变量表、操作数栈、动态链接、方法出口等信息。

本地方法栈
本地方法栈的功能和虚拟机栈类似,本地方法栈为虚拟机用到的Native方法服务,一个Native Method就是一个java调用非java代码的接口,本地方法栈也会抛出StackOverFlow和OutOfMemoryError异常

java堆
java堆可以说是java虚拟机中所管理的内存最大的一块,java堆被所有线程共享,虚拟机启动的时候创建。java堆中存放的对象实例的数组。几乎所有的对象实例以及数组都在堆上分配,java堆也是垃圾回收器管理的主要区域,java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,这和磁盘空间很相似。当一个堆无法再扩展时,会抛出OutOfMemoryError异常。

方法区
方法区也是线程共享的内存区域,用于存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,java虚拟机对方法区的限制十分宽松,和java堆一样不需要连续的内存外,还可以选择不实现垃圾回收。

(二)Java虚拟机栈与程序计数器

虚拟机栈中保存的主要内容是栈帧,每次方法调用就会使得一个栈帧被压入到虚拟机栈。比如上一篇文章讲JVM字节码的时候,我们用到了这样一段代码:

public class Main {
    public static int calculate(){
        int a=1;
        int b=2;
        int c=(a+b)*10;
        return c;
    }
    public static void main(String[] args) {
        System.out.println(calculate());
    }
}

在JVM虚拟机栈中,结构就是这样的:

线程栈

局部变量表存放了编译器可知的八个基本数据类型、对象引用、和returnAddress类型(指向了一条字节码指令的地址)。

操作数栈主要用来进行一系列出入栈的数值操作。

有关局部变量表、操作数栈、程序计数器更加详细的应用可以看我的前一篇文章:两张图让你快速读懂JVM字节码指令

动态链接:动态链接是值在程序运行期间将符号引用转换为直接引用。在一段代码中,类名、常量名、修饰符、对象名等都是符号引用,而如何通过符号找到具体的引用就需要动态链接做一层转换,将符号引用转换为直接引用。比如:JVM就能通过对象名就链接到堆中真实的对象。

动态链接的工作内容和类加载过程中解析这一步是一样的,只不过解析是将一些静态方法或变量(比如main()方法、static变量)替换为指向数据所存内存的直接引用。

方法出口:当一个方法执行之后,返回的方式可能是正常执行结束返回,也可能是抛出异常的返回。无论采用哪种方式,在方法退出之后都需要回到方法调用的位置,因此这些信息就会被保存到方法出口中。

JVM模式每个线程的虚拟机栈大小是1M,也可以通过-Xss调整虚拟机栈的大小。

(三)方法区

方法区是线程共享的内存区域,用于存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。关于方法区的概念,很多人其实都是浑的。方法区、永久代、元空间之间的区别大家都知道吗?

其实方法区是JVM规范中的一个概念,在不同的Java虚拟机以及不同版本的Java虚拟机中,方法区都有各自的实现。以最流行的HotSpot 虚拟机为例,在JDK1.8之前,方法区的实现叫做永久代,放在JVM内存之中。到了JDK1.8,方法区的实现变成了元空间,放在直接内存中。

(四)本地方法栈

本地方法栈的作用是在Java中调用非Java代码,他的功能和虚拟机栈类似。本地方法栈的存在应该算是历史遗留问题,Java刚出来的时候C、C++是当时绝对的统治者,因此在Java中免不了会去调用这一类代码,也因此需要本地方法栈。

(五)堆

堆是JVM虚拟机中最核心的部分了,绝大部分的垃圾回收都在堆中完成。

JVM堆

JVM堆分为新生代和老年代,默认比例为1:2,其中新生代又分为Eden区和两个survivor区,比例为8:1:1。关于堆的大部分内容我已经放到垃圾回收部分进行讲解。

(六)总结

当真正去深入理解JVM虚拟机的时候,会发现它并不像想象中那么难,一个JVM虚拟机实际上就包含了这几样东西,最后再画张图描述一下常用JVM参数针对的位置。

JVM参数针对的位置

相关文章:

  • HDOJ(HDU) 2519 新生晚会(组合公式)
  • 从JVM角度思考--如何预估线上环境机器资源大小
  • 1到3年的Java开发工程师应该如何准备面试
  • 写了两年代码之后再来看看Spring中的Bean
  • 【Python之旅】第二篇(四):字典
  • 使用Optional更优雅地处理非空判断
  • 你能保证你的代码没有异常吗?
  • OC之NSString/NSMutableString
  • 公司CEO和我说:在系统优化的时候,不要轻易用多线程
  • phalapi-进阶篇6(解决大量数据存储数据库分表分库拓展)
  • 快速理解工厂方法模式,及其在源码中的应用
  • 线上报了内存溢出异常,又不完全是内存溢出
  • 用代码告诉你“问世间情为何物,直教人生死相许”
  • 互联网公司的完整开发流程是怎样的?
  • 如何在SpringBoot启动时执行初始化操作,两个简单接口就可以实现
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • JavaScript标准库系列——Math对象和Date对象(二)
  • LeetCode29.两数相除 JavaScript
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • mysql innodb 索引使用指南
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • Sass Day-01
  • Theano - 导数
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • vue学习系列(二)vue-cli
  • 给初学者:JavaScript 中数组操作注意点
  • 构建工具 - 收藏集 - 掘金
  • 蓝海存储开关机注意事项总结
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 如何进阶一名有竞争力的程序员?
  • 如何用vue打造一个移动端音乐播放器
  • 携程小程序初体验
  • 以太坊客户端Geth命令参数详解
  • 自制字幕遮挡器
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • NLPIR智能语义技术让大数据挖掘更简单
  • puppet连载22:define用法
  • $L^p$ 调和函数恒为零
  • (10)STL算法之搜索(二) 二分查找
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (阿里云万网)-域名注册购买实名流程
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (规划)24届春招和25届暑假实习路线准备规划
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (算法二)滑动窗口
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .net core 6 redis操作类
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET gRPC 和RESTful简单对比
  • .NET Micro Framework初体验(二)
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET设计模式(11):组合模式(Composite Pattern)
  • @Autowired和@Resource的区别
  • @configuration注解_2w字长文给你讲透了配置类为什么要添加 @Configuration注解