01_JVM基础及内存区域
JVM及Java内存区域
- JVM基础知识
- Java程序运行流程
- JVM、JRE、JDK的关系
- Java跨语言及跨平台
- 常见JVM实现
- Java内存区域
- 运行时数据区
- 程序计数器
- 虚拟机栈
- 本地方法栈
- Java堆
- 方法区
- 运行时常量池
- 直接内存
这里的概念性解释大多出自于 《深入理解Java虚拟机》
2021年5月12日 第一版
JVM基础知识
Java程序运行流程
一个Java程序,首先通过javac命令将Java源文件编译成.class后缀的class文件,通过JVM加载到方法区,执行引擎执行这些代码时,会翻译成操作系统相关的函数。JVM相当于一个翻译,解释class文件中的字节码调用操作系统的函数。
Java程序执行流程如图1所示:
图1 Java程序执行流程
JVM、JRE、JDK的关系
JVM:Java Virtual Machine,Java虚拟机,将class文件翻译成对应操作系统可识别的内容。
JRE:Java Runtime Environment。Java运行时环境,但有JVM并不能执行java代码,还需要一些核心类库,JRE就是核心类库+JVM。
JDK:Java Development Kit。Java开发工具包,仅有JRE并不能满足开发人员的使用,还需要对代码进行调试、打包、反编译等工具,JDK就是JRE+开发工具。
JVM、JRE、JDK关系图如图2所示:
![](/assets/blank.gif)
图2 JVM、JRE、JDK关系
Java跨语言及跨平台
跨语言:JVM识别的是字节码,而不是Java语言,JVM是和语言解耦的,Groovy、Kotlin、Scala等语言也可以在JVM上运行,Java的跨语言性一定程度上奠定了非常强大的java语言生态圈。
跨平台:写一个类可以在不同操作系统(Windows、Linux、MacOs、Solaris)上运行,执行结果是一致的。跨平台的实现是JVM在不同的操作系统都有单独的实现。
JDK下载地址:
https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
常见JVM实现
- Hotspot:使用最多的虚拟机,Oracle公司。
- Jrocket:原属于BEA公司,号称世界上最快的JVM,后被Oracle收购,合并于Hotspot。
- J9:IBM有自己的java虚拟机实现,它的名字叫做J9.主要是用在IBM产品。
- TaobaoVM:Hotspot的定制版,专门为淘宝准备的,阿里、天猫都是用的这款虚拟机。
- LiquidVM:针对硬件的虚拟机,它下面是没有操作系统的(不是Linux也不是windows),下面直接就是硬件,运行效率比较高
- Zing:属于Zual,是一款商业产品,垃圾回收速度非常快(1毫秒之内),它的一个垃圾回收的算法后来被Hotspot吸收才有了现在的ZGC.
Java内存区域
运行时数据区
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域。如图3所示。
![](/assets/blank.gif)
图3 运行时数据区
程序计数器
程序计数器(ProgramCounterRegister)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码指示器通过改变这个计数器的值来读取下一条需要执行的指令。程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈
Java虚拟机栈(JavaVirtualMachineStack)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(StackFrame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
虚拟机栈异常有两种:StackOverflowError(栈深度超过了虚拟机允许的最大深度)和OutOfMemberError(内存溢出)
局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小(大小指的是变量槽的数量)。
非静态方法中,局部变量表 0索引slot默认为this关键字。
public static void staticMethod();descriptor: ()Vflags: ACC_PUBLIC, ACC_STATICCode:stack=1, locals=1, args_size=00: iconst_11: istore_02: returnLineNumberTable:line 18: 0line 19: 2LocalVariableTable:Start Length Slot Name Signature2 1 0 a Ipublic void normalMethod();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=2, args_size=10: iconst_11: istore_12: returnLineNumberTable:line 21: 0line 22: 2LocalVariableTable:Start Length Slot Name Signature0 3 0 this Lothers/test/VarTest;2 1 1 a I
本地方法栈
本地方法栈(NativeMethodStacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
Java堆
Java堆(JavaHeap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。在《Java虚拟机规范》中对Java堆的描述是:“所有的对象实例以及数组都应当在堆上分配。由于即时编译技术的进步,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生,所以说Java对象实例都分配在堆上也渐渐变得不是那么绝对了。
根据《Java虚拟机规范》的规定,Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的,这点就像我们用磁盘空间去存储文件一样,并不要求每个文件都连续存放。但对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的内存空间。
ava堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。
再次仅简单介绍堆,堆内存的划分及逃逸分析技术相关细节则在之后文章中详细介绍。
方法区
方法区(MethodArea)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型(类)信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫作“非堆”(Non-Heap),目的是与Java堆区分开来。
简单区分下方法区、元空间和永久代之间的区别,方法区是一个虚拟机规范的概念,不是具体的实现,Hotspot在JDK8之前,使用永久代来实现方法区,这样使得HotSpot的垃圾收集器能够像管理Java堆一样管理这部分内存,省去专门为方法区编写内存管理代码的工作。但是对于其他虚拟机并不存在永久代的概念。JDK8完全放弃了永久代的概念,改用在本地内存中实现的元空间来代替。
根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
运行时常量池
运行时常量池(RuntimeConstantPool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(ConstantPoolTable),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
常量池方面有很多争议,尤其是字符串常量池,这个之后文章中会根据自己的理解来详细的说一下。
直接内存
直接内存(DirectMemory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。
关于直接内存的大小问题,大部分网上说真的直接内存默认为64MB,System类中 sun.misc.VM.saveAndRemoveProperties(props); 这一行代码上的注释会给出一个比较更可靠的结论,直接内存最大值在不指定的时候默认值是从JVM内部获得的。
直接内存可以通过参数设置大小,-XX:MaxDirectMemorySize,默认值为…挖个坑,以后再说。
JVM的直接内存虽然不属于JVM自动内存管理的区域,但是使用Java中的接口使用直接内存,垃圾回收时,还是可以将这部分的内存给回收掉的
01_JVM基础及内存区域相关推荐
- java基础---JVM---java内存区域与内存溢出问题
===?虚拟机运行时候内存主要有几块区域,是线程相关的吗,分别有什么作用? 程序计数器:线程私有,用来记录当前线程所执行的字节码的行数,使得在多线程执行的情况下线程切换后能回到正确的执行位置.程序计数 ...
- java内存四大区,jvm基础-内存区域
1.运行时数据区 java虚拟机在执行java程序的过程中会爸它所管理的内存分为若干个不同的数据区域 jvm内存主要分为堆.程序计数器.方法区.虚拟机栈喝本地方法栈,直接内存等. java方法的运行和 ...
- 基础面试题:java内存区域
JVM 内存区域主要分为线程私有区域[程序计数器.虚拟机栈.本地方法区].线程共享区域[JAVA 堆.方法区].直接内存. 线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销 ...
- jvm虚拟机基础知识--、java内存区域(1)
JAVA程序运行过程 jvm的跨平台和语言无关性 所有的java程序都需要编译成class文件的形式提交给jvm虚拟机来加载,通过图可以看到class文件经过javac编译以后进入jvm虚拟机内部首先 ...
- 【JAVA 学习基础】java内存区域 探究
前言 文章仅是笔者个人的学习笔记,存在一些只有笔者个人能看到的用词或者描述,如果有不明确的地方,欢迎留言,理性讨论. 一.概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外 ...
- 深入理解Java虚拟机——第二章——Java内存区域与内存溢出异常
运行时数据区域 Java虚拟机运行时数据区域 程序计数器 程序计数器可以看做是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过改变这个计数器的值来选取下一条所需要执行的字节码指令,分支. ...
- java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...
2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...
- 深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析
来自:好好学java 这篇文章主要介绍Java内存区域,也是作为Java虚拟机的一些最基本的知识,理解了这些知识之后,才能更好的进行Jvm调优或者更加深入的学习,本来这些知识是晦涩难懂的,所以希望能够 ...
- 学习jvm(一)--java内存区域
前言 通过学习深入理解java虚拟机的教程,以及自己在网上的查询的资料,做一个对jvm学习过程中的小总结. 本文章内容首先讲解java的内存分布区域,之后讲内存的分配原则以及内存的监控工具.再下来会着 ...
最新文章
- VS2017编译可在Win2000上运行的程序
- calico的两种网络模式BGP和IP-IP性能分析
- mybatisplus 一次性执行多条SQL语句插入(Oracle篇)
- matlab dct稀疏系数,Matlab DCT详解
- es安装IK中文分词器
- Python数据结构实战—栈(Stack)
- JAVA锁和volatile的内存语义volatile的使用场景
- 调用企业微信API给用户发消息
- WordPress发布插件,WordPress定时采集伪原创发布插件
- 51单片机串口中断控制灯
- 斑马Zebra LP2844 打印机驱动
- 数据库系统概论判断题
- 即将前往下一个饭局,你的牙还好吗?丨钛空舱爆款春节特献
- word生成目录右对齐
- 头部 CT 图像三维重建
- 使用 Arduino 和电位器控制直流电机速度
- vue+springboot+java志愿者活动报名网站系统maven源码
- 通过RViz中的InteractiveMarkers在ROS中仿真力和力矩(wrench.force和wrench.torque)
- 【Kaggle】成绩为0分的情况
- C - 鬼吹灯之龙岭迷窟
热门文章
- 小学学校计算机设备统计表,中小学实验教学仪器设备统计表新
- Python爬虫圈最抗打的专栏教程,《Python爬虫120例》教程导航帖(2022.10.7更新)
- [由零开始]Linux安装Rdeis集群 完整步骤
- python三维重建代码_基于Python的三维重建开源代码
- Android修改系统时间
- linux云同步文件夹,在Linux系统上备份Dropbox同步文件夹的方法
- python期货程序化交易高手心得_程序化交易高手的交易心得 分享~
- WebStorm 10.0.4最新汉化包下载
- ReSharper入门(一) 【含:详细图文攻略】
- 混淆矩阵及其评价指标