Java虚拟机 之 内存区域
引言
上一篇介绍了JVM的发展史,那只是简单的看了JVM,只是知道有这么一个东西,是干什么用的;从这一篇到未来,将一点点慢慢了解它,为的是为以后的学习打下基础和浇灭面试官的狂气 :)。
简介
下面是我画的一个草图,可以表示出大致的结构图,但不是详细的。
那我们平时所说的堆内存和栈内存到底是什么呢?
堆内存:就是指的Java堆;
栈内存:虚拟机栈中的局部变量表。
线程独占区与线程共享区
首先得知道,每一个线程都是一个顺序的执行单元。
线程独占区:就是对每一个线程都有这样一块区域(比如说有自己的程序计数器、有自己的本地方法栈和虚拟机栈)。
线程共享区:多个线程共享这同一块区域。
在一个程序中,会存在多个线程;
在每一个线程中,存在着自己的线程独占区,这里就有他们的程序计数器、本地方法栈……;
而方法区和堆处在线程共享区,被所有线程所共享。
各区域的介绍
知道有这些东西,那他们都是干什么的呢?先简单说一下,然后挨个区域再详细说。
共享:
方法区:存储运行时常量池、已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
Java堆:存储实例对象。
独占:
虚拟机栈:存储方法运行时所需的数据,成为栈帧,虚拟机栈描述的是方法的内存模型。
本地方法栈:为JVM所调用的Native,即本地方法服务。
程序计数器:记录当前线程所执行到的字节码行号。
程序计数器
1. 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
字节码是什么呢?字节码(Byte-code)是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件,是一种中间码。
2. 程序计数器处于线程独占区,每一个线程都会有它的程序计数器。
3. 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器的值为undefined。
4. 此区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域,我们不需要操作程序计数器,这个计数器是JVM内部维护的。
5. Java中保留字"goto",作用是跳到某一行。goto目前java中不使用,也不允许开发者使用,为的就是可能在以后的JDK版本中增加这样一个关键字。
Java虚拟机栈
虚拟机栈描述的是Java方法执行的动态内存模型。
栈帧:每个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成;栈帧是用来存储方法所执行的局部变量表、操作数栈、动态链接、以及方法出口等;遵守"先进后出,后进先出"的原则。
首先,每一个方法的执行都会创建一个栈帧。
当我门创建好了栈帧,就开始进栈了,进栈也就开始执行这个方法。
如果,在执行的过程中,它调用了另外的一个方法,而另一个方法也需要创建一个栈帧。
接着,它所调用的方法进栈,这个方法就开始执行了。
当那个方法执行到方法出口之后,这个方法就执行完毕了,相应的栈帧也就出栈了,出栈之后,栈帧随之销毁了。
然后接着执行第一个方法,等方法执行结束后,同样出栈。一个Java方法的执行过程就结束了。
局部变量表:
1. 存放编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double);对象引用(不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置);returnAddress类型(指向一条字节码指令的地址)。
2. 局部变量表所需的内存空间在编译期间分配完成,当进入一个方法时,这个方法需要在栈中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
3. 假如说有一个对象user,而在user里面有一个String类型的属性name。那么它在运行期间可能会给他指定值,它的长度一定是不固定的,为什么说他的内存区域是不会变化的呢?因为我们在局部变量表中所引用的只是对象的引用,对象的创建会创建到堆内存中,而局部变量表他所存储的就是这个对象的引用,这个对象的引用的大小他是不会改变的,所以,在方法运行期间局部变量表的大小是不会改变的。
Java虚拟机栈的大小与抛出的异常:
1. StackOverFlowError(栈内存溢出)。由于方法的不停进栈,从而导致错误抛出。
2. OutOfMemoryError(内存溢出)。而如果这个栈的内存大于虚拟机的内存,或者说已经超出了机器本身的内存,并且已经申请不下内存来的时候。
本地方法栈
本地方法栈与虚拟机栈相似,栈帧、栈大小异常抛出、局部变量表等都完全相同,以至于很多虚拟机把两者合二为一。
两者唯一的区别就是 虚拟机栈为虚拟机执行Java方法服务;本地方法栈为所使用的Native方法服务。
Java堆
1. 唯一目的:存放对象实例。
2. 一般来说,Java堆是JVM所管理的最大的一块内存区域。因为在应用运行过程中,应用会不停地创建对象,只要是对象的存储,都会放到堆中。
3. Java堆是垃圾收集器所管理的主要区域,也是回收效率最高的。
4. 从回收角度看,大致可分为新生代和老年代;从分配角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。无论如何划分,存储的都是对象实例,目的是为了更好地回收内存或更快地分配内存。
5. Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
6. 如果堆中没有内存完成实例的分配了,则会抛出OutOfMemoryError。
7. 修改堆大小的两个参数:-Xmx 和 -Xms。
方法区
1. 前面说的,方法区是存储运行时常量池、已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
那什么是类信息?
类信息就是指类的版本、字段、方法、接口等。
2. 在Java虚拟机规范中,方法区和永久代并不是等价的。只是在HotSpot虚拟机中,为了减少内存管理代码的工作,而省去了专门对方法区的编码。
3. 垃圾收集器主要在Java堆回收,而方法区是堆的一个逻辑部分,但垃圾收集器在方法区出现较少,因为它回收效率较低。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
4. 当申请内存区域失败,仍会抛出OutOfMemoryError。
运行时常量池
1. 运行常量池属于方法区的一部分。
2. 它用来存储编译期生成的各种字面量和符号引用。
3. 现有s1,s2:
String s1 = "abc";String s2 = "abc";
问:(s1 == s2)结果如何?
答:“==”是表示地址是否相同。
随着方法的运行,我们会对每一个方法创建一个栈帧,栈帧里有一个局部变量表,所有的基本数据类型和抽象数据类型的引用都会放到局部变量表中。
由于HashSet是无序、不可重复的。“abc”存入到运行时常量池后,到第二个“abc”也只会存入一个,因为只创建了一个实例,那么,s1和s2的地址显然是相同的。
故,“s1 == s2”为true。
4. 现有s2,s3:
String s2 = "abc";String s3 = new String("abc");
问:(s2 == s3)结果如何?
答:如果使用new去创建对象的话,它一定是在堆内存,它不再去考虑常量池。所以,直接在堆中创建了一个s3。
s3在堆中,s2在常量池中,显然不是一块内存区域,内存地址也肯定是不相同的。
故false。
5. 现有s1,s3:
String s1 = “abc”;String s3 = new String("abc");
问:“s1 == s3.intern()”
答:intern()这个方法,它会把堆中的区域搬到运行时常量池中去,产生一个运行时常量。
故为true。
直接内存
它是一种基于通道与缓冲区的I/O方式,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java 堆中的DirectByBuffer对象作为这块内存的引用进行操作。
好处就是避免在Java堆和Native堆中来回复制数据,提高性能;
但在配置虚拟机参数时容易被忽略掉,让各内存区域总和大于实际内存,从而抛出OutOfMemoryError异常。
Java虚拟机 之 内存区域相关推荐
- Java虚拟机------JVM内存区域
JVM内存区域运行时数据区域分为两种: JVM内存区域 运行时数据区域分为两种: 线程隔离的数据区: 程序计数器 Java虚拟机栈 本地方法栈 所有线程程共享的数据区: Java堆 方法区 JVM 内 ...
- JAVA虚拟机 安全区域_Java虚拟机的内存区域
2020年12月10日 阅读 186 关注 Java虚拟机的内存区域 最近在看<深入理解Java虚拟机>,故此写下自己的学习笔记. JVM 运行时数据区域 Java 虚拟机在执行 Java ...
- 深入理解java虚拟机一 JAVA运行时内存区域与class文件
一 JAVA运行时内存区域 JVM在加载class文件时,会将class文件定义的数据结构转为运行时内存中的数据,那么jvm是如何安排运行时的内存区域呢? jvm将运行时内存划分为以下几个部分: 堆: ...
- 写java线程导致电脑内存不足_如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码...
程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...
- Java虚拟机自动内存管理
生活规律告诉我们,在享受便利的同时一般都会付出巨大的代价,如果你在享受了便利的同时,还没有为此付出代价,不是说明没有,只是还没到付出的时候.试问,有哪个Java系统架构师不懂Java虚拟机?纵观Jav ...
- 深入理解java虚拟机【内存溢出实例】
通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收 ...
- 深入理解Java虚拟机2——内存管理机制及工具
一.内存区域与内存溢出异常 1. Java虚拟机的运行时数据区: ----------------------------------- 运行时区域 方法区 虚拟机栈 本地方法栈 堆 程序计数器 -- ...
- 学习笔记【Java 虚拟机④】内存模型
若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 总目录 学习笔记[Java 虚拟机①]内存结构 学习笔记[Java 虚拟机②]垃圾回收 学习笔记[Java ...
- Java虚拟机的内存空间有几种
Java虚拟机的内存空间有几种?(1)问题分析: JVM(虚拟机)的内存划分 不同的数据使用的是哪一块内存空间 (2)核心答案讲解: Java虚拟机有那几块内存空间: 1)栈内存:方法运行时所进入的内 ...
最新文章
- java/android 设计模式学习笔记(1)--- 单例模式
- Struts2 源码分析——拦截器的机制
- php获取citypicker的值,城市选择city-picker
- Gradle入门:依赖管理
- linux查端口被占用情况,Linux系统中如何查询端口被占用情况
- 需要重新启动php,win10电脑遇到问题要重新启动怎么回事
- Ms08-067漏洞抓鸡 方法
- Chrome的最小字体12px限制最终解决办法
- Sonic 云真机测试平台1.3.2-release版本搭建
- android ppt的动画效果怎么做,Android 仿 PPT 进入动画效果合集
- 英语口语收集(二十六)
- 线性代数学习笔记——第十二讲——求解矩阵方程
- Linux命令:configure --prefix=/ 有什么作用
- python画双y轴图像
- R语言入门——数据快速读取与查看(含实例代码和参数讲解)
- python中常用的utils
- PySCENIC(二):pyscenic单细胞转录组转录因子分析
- 程序设计与算法MOOC021:鸣人与佐助(C++DFS、剪枝)
- VRRP和DHCP的设置
- 拓扑排序在实际项目中应用