您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

图解Java内存区域

时间:2020-03-06 16:04:05  来源:  作者:

JAVA是一座围城,Java开发不需要像C、C++开发人员那样,维护每个对象从开始到终结的职责。因为Java虚拟机会帮助我们完成这些职责,但是一旦发生内存泄漏和溢出,就需要我们排查。

图解Java内存区域

 

Java虚拟机执行Java程序时,把它管理的整个内存区域称为运行时数据区。同时根据区域的用途,以及创建和销毁时间等因素,将运行时数据区分成不同的区域。

图解Java内存区域

 

程序计数器

程序计数器表示当前线程所执行字节码指令的行号计数器。字节码解释器通过改变程序计数器的值,选取下一条需要执行的指令。为了保证线程切换之后恢复到正确的执行位置,每条线程都需要独立的程序计数器,所以程序计数器是线程私有的。同时程序计数器是唯一一个在虚拟机规范中没有规定 OutOfMemoryError 的区域。

注:线程执行Java方法,程序计数器记录字节码指令地址;如果执行的是本地(Native)方法,程序计数器为空。

图解Java内存区域

 

虚拟机栈

虚拟机栈是Java方法执行的线程内存模型。每个方法的执行,Java虚拟机都会创建一个栈帧存储方法相关变量。每个方法被调用到执行完毕的过程,对应栈帧在虚拟机栈中入栈到出栈的过程。

如下图所示,当虚拟机执行 swap(a,b) 方法时,会创建一个单独的栈帧 swap(a,b) 栈帧,在该栈帧中会存储于方法相关的变量,该栈帧的入栈和出栈操作对应着方法的执行和结束。

图解Java内存区域

 

每个栈帧都包含了局部变量表、操作数、动态链接、方法返回值。

  • 局部变量表:存放方法参数和内部定义的局部变量。局部变量表的容量以变量槽为最小单位每个变量槽可以存放一个 boolean 、 byte 、 char 、 short 、 int 、 float 、 reference 、 returnAddress 数据类型。
  • 操作数栈:底层也是栈结构,是进行数据运算的地方。当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。
  • 动态链接:将常量池中指向方法的部分符号引用,在方法运行期间转为直接引用。字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
  • 返回地址:方法执行退出后,返回到方法被调用的地方。
图解Java内存区域

 

在 swap 函数执行的过程中, a 、 b 、 temp 都会保存到局部变量表中,其中的赋值操作则通过操作数栈执行,

方法执行完毕返回到调用的地方的地址则存储在返回地址中。

图解Java内存区域

 

本地方法栈

本地方法栈与 Java 虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法 服务。

Java堆

Java堆是虚拟机管理的内存中最大的一块,几乎所有对象都在Java堆分配内存。Java堆在虚拟机启动的时候创建,被所有的线程共享。Java堆也会涉及到内存回收的内容,本片文章先不展开了。Java堆无法扩展时,会报出 OutOfMemoryError 异常。

图解Java内存区域

 

方法区

方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码缓存等数据。方法区是各个线程共享的内存区域。

图解Java内存区域

 

屏幕面前的你,会不会遇到这样的困惑。方法区和永久代有什么关系?和元空间呢?

  • 方法区和永久代的关系方法区是JVM规范概念,而永久代则是HotSpot虚拟机特有的概念。《Java虚拟机规范》只是规定了有方法区的概念和作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 同时大多数用的JVM都是Sun公司的HotSpot。在HotSpot上把GC分代收集扩展至方法区,或者说使用永久代来实现方法区。因此永久代是HotSpot的概念,方法区是Java虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现。其他的虚拟机实现并没有永久带这一说法。在1.7之前在(JDK1.2 ~ JDK6)的实现中,HotSpot 使用永久代实现方法区,HotSpot 使用 GC分代来实现方法区内存回收,可以使用如下参数来调节方法区的大小。
  • 元空间对于Java8, HotSpots取消了永久代,取代永久代的就是元空间。永久代存在内存上限( -XX:MaxPermSize ,即使不设置也有默认大小),当进程申请不到足够的内存,会造成内存溢出。改成元空间后,改用本地内存,只要本地空间足够,就不会有内存溢出的问题。元空间和永久代有什么不同的?存储位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;存储内容不同,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中。

运行时常量池

运行时常量池是方法区的一部分,是一块内存区域。Class 文件常量池将在类加载后进入方法区的运行时常量池中存放。 一个类加载到 JVM 中后对应一个运行时常量池。

图解Java内存区域

 

易混淆的概念

屏幕面前的你,会不会遇到这样的困惑。运行时常量池和Class文件常量池有什么关系?和字符串常量池呢?和缓冲池呢?

Class文件常量池

Class 文件常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。常量池中主要存放两大类常量:字面量和符号引用。当Class文件常量池加载到方法区时,会把符号引用转换为直接引用,存放到运行时常量池。

图解Java内存区域

 


图解Java内存区域

 

字符串常量池

字符串常量池是 全局的, JVM 中独此一份 ,因此也称为全局字符串常量池。

其中:

在 jdk1.6(含) 之前也是方法区的一部分,并且其中存放的是字符串的实例; 在 jdk1.7(含) 之后是在堆内存之中, 存储的是字符串对象的引用,字符串实例是在堆中;

底层原理

在 HotSpot VM 里实现线程池功能的是一个 StringTable 类,它是一个Hash表,默认值大小长度是1009;这个 StringTable 在每个 HotSpot VM 的实例只有一份,被所有的类共享。字符串常量由一个一个字符组成,放在了 StringTable 上。

String str1 = "图解Java";
String str2 = new String("图解Java");
System.out.println(str1 == str2);

在这段代码中,当执行 String str1 = "图解Java" 时,先到常量池中查询有没有 "图解Java" 字符串的引用,如果没有,则会在 Java堆 上创建 "图解Java" 字符串,在常量池中存储字符串的地址, str1 则指向字符串常量池的地址。

String str2 = new String("图解Java") ,则会直接在Java堆中创建对象。 str2 指向堆中的地址。

看到这里,屏幕面前的你有没有想到最后的结果是 false 呢。

如果此时还有 String str3 = "图解Java" 那么 str1==str3 的结果是什么?

此时 str3 发现字符串常量池中已经有了 "图解Java" 字符串的引用,则直接返回,不会创建新的对象。

看到这里,屏幕面前的你有没有想到最后的结果是 true 呢。

图解Java内存区域

 

JVM 中除了字符串常量池,8种基本数据类型中除了两种浮点类型剩余的6种基本数据类型的包装类,都使用了缓冲池技术,但是 Byte 、 Short 、 Integer 、 Long 、 Character 这5种整型的包装类也只是在对应值在 [-128,127] 时才会使用缓冲池,超出此范围仍然会去创建新的对象。

Class文件常量池、运行时常量池、字符串常量池的联系

我们平时写好的Java代码即Java格式的文件,经过编译,会变成Class类型的文件。而Class文件有一部分是Class文件常量池,用于存储字面量和符号引用。

Class文件经过类加载器加载后,之前Class文件常量池的内容会存放到方法区的运行时常量池,需要注意的是Class文件常量池的符号引用会转变直接引用存入运行时常量池。

字符串常量池是 JVM 的一部分,整个 JVM 只有一份,在将Class文件常量池的字面量也会在类加载的时候进入到字符串常量池中。

份数内容Class文件常量池每个类对应一份字面量、符号引用运行时常量池每个类对应一份字面量、直接引用字符串常量池整个 JVM 仅有一份字符串

图解Java内存区域

 



Tags:Java内存   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
导读本文介绍Java诸多优化实例:第一,排查堆上、堆外内存泄露;第二,使用arthas、jaeger、tcpdump、jstack做性能优化;第三,排查进程异常退出的原因,如被杀、System.exit、Java调用的...【详细内容】
2020-11-25  Tags: Java内存  点击:(78)  评论:(0)  加入收藏
导读本文介绍Java诸多优化实例:第一,排查堆上、堆外内存泄露;第二,使用arthas、jaeger、tcpdump、jstack做性能优化;第三,排查进程异常退出的原因,如被杀、System.exit、Java调用的...【详细内容】
2020-08-11  Tags: Java内存  点击:(50)  评论:(0)  加入收藏
Java是一座围城,Java开发不需要像C、C++开发人员那样,维护每个对象从开始到终结的职责。因为Java虚拟机会帮助我们完成这些职责,但是一旦发生内存泄漏和溢出,就需要我们排查。 J...【详细内容】
2020-03-06  Tags: Java内存  点击:(52)  评论:(0)  加入收藏
内存泄漏定义(memory leak):一个不再被程序使用的对象或变量还在内存中占有存储空间。一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。 内存溢出 out o...【详细内容】
2020-02-24  Tags: Java内存  点击:(71)  评论:(0)  加入收藏
运行时数据区域Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。Java虚拟机所管理的内存包括如下几个部分: 程序计数器程序计数器是一块较...【详细内容】
2020-02-23  Tags: Java内存  点击:(43)  评论:(0)  加入收藏
前言首先我们在了解java内存模型之前先看一下计算机内存模型,理解了计算机内存模型的话后面在看JMM就会简单的多。 计算机内存计算机是由CPU、主存、磁盘等组成的(简单引出问...【详细内容】
2019-10-14  Tags: Java内存  点击:(72)  评论:(0)  加入收藏
内存映射文件(Memory-mapped File),指的是将一段虚拟内存逐字节映射于一个文件,使得应用程序处理文件如同访问主内存(但在真正使用到这些数据前却不会消耗物理内存,也不会有读写...【详细内容】
2019-08-14  Tags: Java内存  点击:(207)  评论:(0)  加入收藏
▌简易百科推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  顶顶架构师    Tags:面向对象   点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Java架构师之路    Tags:JAVA   点击:(5)  评论:(0)  加入收藏
大家好!我是老码农,一个喜欢技术、爱分享的同学,从今天开始和大家持续分享JVM调优方面的经验。JVM调优是个大话题,涉及的知识点很庞大 Java内存模型 垃圾回收机制 各种工具使用 ...【详细内容】
2021-12-23  小码匠和老码农    Tags:JVM调优   点击:(12)  评论:(0)  加入收藏
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  dingle    Tags:JDBC   点击:(13)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  JAVA小白    Tags:Java   点击:(11)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  小西学JAVA    Tags:JAVA并发   点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  唯一浩哥    Tags:Java基础   点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  软件老王    Tags:logback   点击:(19)  评论:(0)  加入收藏
本篇文章我们以AtomicInteger为例子,主要讲解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe对象。我们先从一个例子开始吧。假设现在我们...【详细内容】
2021-12-14  小西学JAVA    Tags:JAVA   点击:(22)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条