当前位置: 首页 - 手机 - 全面剖析Java内存区域和常量池

全面剖析Java内存区域和常量池

2024-12-12 手机 0

全面剖析Java内存区域和常量池

本文用最简洁的描述,来总结出Java内存区域和常量池的相关知识,如需更加深入学习Java内存区域以及常量池,可参考阅读《深入Java虚拟机器》或者网上优秀博文。

执行时资料区

执行资料区包含以下几个区域:

方法区(Method Area)Java堆(Heap)本地方法栈(Native Method Stack)虚拟机器栈(VM Stack)程式计数器(Program Conter Register)其中方法区和堆是所有执行绪共享的资料区,而其他三个区收拾执行绪隔离的资料区。

Java虚拟机器在执行Java程式的过程中将它管理的内存划分为若干个不同的区域,这些区域都拥有各自的用途以及建立和销毁的时间。

方法区和堆都是依赖虚拟机器执行绪的启动而建立;本地方法栈、虚拟机器栈和程式计数器都依赖使用者执行绪的启动和结束而建立和销毁。

程式计数器

该区是一块较小的内存空间。可以看作是字节码的行号指示器。分支、循环、跳转、异常处理、执行绪恢复等基础工作都是基于程式计数器来完成的,因为字节码直译器工作的时候,就是根据程式计数器的值来确认下一条需要执行的字节码子令。该区是执行绪独立的,被称为“执行绪私有”的内存。该区域是唯一一个在Java虚拟机器规范中没有规定任何OOM(OutOfMemoryError)情况的区域。Java虚拟机器栈

该区是执行绪私有的。Java虚拟机器栈描述的是Java方法的内存模型。该区就是所谓的“栈内存”,实际上Java内存不可粗糙的分为堆内存和栈内存,因为Java内存区域的划分比这复杂的多。该区会丢掷StackOverflowError异常和OOM(OutOfMemoryError)异常;当执行绪请求的栈深度大于虚拟机器所允许的深度时,会丢掷栈溢位异常。因为虚拟机器栈是可以动态扩充套件的,所以当虚拟机器栈无法申请到足够的内存时,就会丢掷OOM异常。关于Java方法的内存模型

每个Java方法在执行的同时,都会建立一个栈帧存放于栈中,而该栈帧中会储存区域性变量表、算子栈、动态连线、方法出口等资讯。当方法结束呼叫,栈帧就会出栈。

区域性变量表

区域性变量表存放:

编译期可知的基本资料型别(boolean、char、byte、short、long、flag、int、double)。物件引用。returnAddress(指向一条字节码指令的地址)。对于区域性变量表,需要注意:

区域性变量表所需的内存空间在编译期就已经完成分配了,当进入一个方法时这个方法需要在栈帧中分配多大的区域性变数空间是完全确定的,在方法执行期间不能改变区域性变量表的大小。本地方法栈

本地方法栈使用到的是Native方法服务,基本的原理和Java虚拟机器栈非常相似。Sun HotSpot将本地方法栈和Java虚拟机器栈合二为一。本地方法栈也会丢掷StackOverflowErrot和OOM异常。Java堆

Java堆是虚拟机器所管理的内存中最大的一块。并且是执行绪共享的,随着虚拟机器执行绪的启动而建立。此区域的唯一目的就是储存物件,几乎所有的例项物件都在该区分配内存,为什么不是全部呢?因为一个类的java.lang.Object类物件是在方法区中分配内存的。几乎所有的物件例项以及阵列都要在堆上分配内存。此区是垃圾收集器管理的主要区域,所以此区也被称为GC堆。Java堆可分为新生代、老年代,新生代又可细分为Eden空间、From Survivor空间、To Survivor空间。堆内存中的区域是物理上不连续的,但逻辑上是连续的内存区域。此区域可拓展,也可固定,一般都是定为可拓展(通过-Xmx和-Xms控制)。堆中如果没有内存分配给例项,并且无法再拓展内存区域了,就会丢掷OOM异常。方法区

方法区是执行绪共享的区域。方法区储存虚拟机器载入的类资讯、常量、静态变数、即时编译器编译后的程式码等资料。方法区被很多人称为“永久代”,因为HotSpot团队选择把GC延伸至方法区。不过现在已经有放弃永久代并逐步改为采用Native Memory来实现方法区的规划了,在JDK1.7的HotSpot中,一把原本放在永久代中的字串常量池移出。内存区域可以是物理上不连续的,但逻辑上是连续的,与堆内存是一致的。方法区因为总是存放不会轻易改变的内容,故又被称之为“永久代”。HotSpot也选择把GC分代收集扩充套件至方法区,但也容易遇到内存溢位问题。可以选择不实现垃圾回收,但如果回收就主要涉及常量池的回收和类的解除安装。该区域无法满足内存分配需求时,会丢掷OOM异常。非执行时资料区——直接内存

直接内存不是执行时资料区的一部分,并不是Java定义规范中的内存区域,但是不合理的使用也会导致OOM异常。JDK 1.4中新加入了NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函式库直接分配堆外内存,然后通过一个储存再Java堆中的DirectByteBuffer物件作为这块内存的引用进行操作。执行时常量池?静态常量池(class档案常量池)?字串常量池?有什么区别?下面一去来看看这个让人容易混淆的三个概念。

Java中的常量池

class档案常量池

我们都知道,class档案中除了包含类的版本、字段、方法、界面等描述资讯外,还有一项资讯就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References),这就是我们所说的class档案常量池。 字面量就是我们所说的常量概念,如文字字串、被宣告为final的常量值等。 符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指标,相对偏移量或是一个能间接定位到目标的控制代码)。一般包括下面三类常量:

类和界面的全限定名字段的名称和描述符方法的名称和描述符(每种常量型别的资料结构可以检视《深入理解java虚拟机器》第六章的内容)

class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量的符号引用。

执行时常量池

执行时常量池是方法区中的一部分。Class档案中除了有类的版本、字段、方法、界面等描述资讯外,还有常量池(Constant Pool Table)。常量池中储存的是编译期生成的各种字面量和符号引用,在JDK1.6及其之前的版本,这部分内容都将在类载入完后进入到方法区的执行时常量池中存放。执行时常量池相比于类常量池的不同特征在于,执行时常量池具备动态性,也就是说不一定要编译期的时候才能产生常量,也可以在执行时产生常量,比如在执行期间呼叫String.intern()方法,也可以将新的常量放入执行时常量池中。执行时常量池是在类载入完成之后,将每个class常量池中的符号引用值转存到执行时常量池中,也就是说,每个class都有一个执行时常量池,类在解析之后,将符号引用替换成直接引用,与全域性常量池中的引用值保持一致。string.intern()作用:

检查字串常量池中是否存在String并返回池里的字串引用;若池中不存在,则将其加入池中,并返回其引用。 这样做主要是为了避免在堆中不断地建立新的字串物件。

字串常量池

字串常量池在每个VM中只有一份,存放的是字串常量的引用值。字串常量池——string pool,也叫做string literal pool。字串池里的内容是在类载入完成,经过验证,准备阶段之后在堆中生成字串物件例项,然后将该字串物件例项的引用值存到string pool中。string pool中存的是引用值而不是具体的例项物件,具体的例项物件是在堆中开辟的一块空间存放的。对于string pool:

在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个杂凑表,里面存的是驻留字串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字串例项本身),也就是说在堆中的某些字串例项被这个StringTable引用之后就等同被赋予了”驻留字串”的身份。这个StringTable在每个HotSpot VM的例项只有一份,被所有的类共享。

JDK不同版本与常量池位置的变化

在JDK1.6版本以前,执行时常量池在方法区中;JDK1.7版本是,执行时常量池是在堆中;JDK1.8时,执行时常量池是在方法区和堆区相对独立的元空间(Metaspace),而不是在堆区。不同版本因为OOM导致的问题: JDK1.6版本——java.lang.OutOfMemoryError: PermGen space; JDK1.7版本——java.lang.OutOfMemoryError:Java heap space; JDK1.8版本——java.lang.OutOfMemoryError: Metaspace;作者:coderbruis

连结:https://juejin.im/post/5c072e8e6fb9a04a0440cd52

感谢您的观看,喜欢的小伙伴可以点个赞!!!专注Java、大资料知识干货及相关领域动态分享,请多多关注哦!

标签: 华为nova5z为什么懂手机的人都不用华为2022手机排行榜国产手机排行榜华为 荣耀

上一篇:互联网模式为中国科技创新带来的变革

下一篇:移动支付革命触摸未来挥洒便捷

相关推荐
推荐资讯
热门文章