JVM虚拟机内存区域详情

发布时间:2024年01月02日

QPS含义
QPS Queries Per Second 是每秒查询率 ,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准, 即每秒的响应请求数,也即是最大吞吐能力。

内存区域详情

运行时数据区域

线程私有:

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

线程共享:

  • 方法区
  • 直接存储(非运行时数据区的一部分)

Java虚拟机规范对于运行时数据区域的规定是相当宽松的。以堆为例,堆空间可以是连续的,也可以是不连续的。堆的大小可以固定,也可以在运行时按需扩展。虚拟机实现者可以使用任意垃圾回收算法管理堆,甚至不进行垃圾回收都可以。

程序计数器

是一段比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

另外,为了线程切换后能恢复到正常的执行位置,每条线程都需要一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

生命周期随线程的创建而创建,随线程的结束而死亡。

Java虚拟机栈

线程私有的,生命周期和线程一样。

1.栈是JVM运行时数据区域的一个核心,除了一些Native方法调用通过本地方法栈实现的,其他所有的Java方法调用都是通过栈来实现的。(当然也需要和其他运行时数据区域相互配合,比如:程序计数器)

2.方法调用数据通过栈进行传递,每次方法调用,都会有一个对应的栈帧被压入到栈中,每次方法调用结束,都会有一个栈帧被弹出。

3.栈是由一个个栈帧组成。每个栈帧都拥有:局部变量表、操作数栈、动作链接、方法返回地址。和数据结构的栈类似,都是先入后出的数据结构,只支持入栈和出栈两种操作。

局部变量表:主要存放编译期可知的各种数据类型(int/short/long/char/byte等)、对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与对象相关的位置).

操作数栈:主要作为方法调用的中转站使用,用来存储方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放到操作数栈中。

动作链接:主要服务一个方法调用其他方法的场景。Class文件的常量池中保存着大量的符号引用,比如方法引用的符号引用。当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转化为调用方法的直接引用,这个过程被称为动作链接。

方法返回地址:两种返回方式,一种是return语句正常返回,一种是抛出异常。不管哪种返回方式,都会导致栈帧被弹出。也就是说,栈帧随着方法的调用而被创建,随着方法的结束而销毁。无论方法方法正常完成还是异常完成,都算方法结束。

StackOverFlowError: 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java
虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。 OutOfMemoryError:
如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

本地方法栈

和虚拟机栈发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈,则为虚拟机使用到的Native方法服务。在HoSpot虚拟机中和Java虚拟机栈合二为一。

Java虚拟机所管理的内存中最大的一块,Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java堆是垃圾回收器管理的主要区域,因此被称为GC堆。

从垃圾回收角度,由于现在的收集器都是采用的分代垃圾收集算法,所以Java堆还可以细分为:新生代、老年代、永久代(JDK7);再细致分:Eden、Survivor、Old等空间。进一步划分的目的是为了更好的回收内存,或者更快的分配内存。
JDK8之后永生代被元空间取代,元空间使用的是本地内存。

虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。 对象在Survivor 区中每熬过一次Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。
对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置。

方法区

属于JVM虚拟机运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。

当虚拟机要使用一个类时,它需要读取并解析Class类文件获取相关信息,再将信息存到方法区。方法区会存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

方法区和永生代及元空间是什么关系?

方法区和永生代以及元空间的关系很像Java中接口和类的关系,类实现了接口,这个类就可以看做永生代和元空间,接口看做方法区,也就是说永生代以及元空间是HotSpot虚拟机对虚拟机规范中方法区的两种实现。并且,永生代是JDK8之前的方法区的实现,元空间是之后的方法区实现。

为什么将永生代替换为元空间?

  1. 整个永生代在JVM中有一个设置本身大小的上线,且不能修改;而元空间使用的是本地内存,受本机可用内存限制,虽然元空间仍可能存在溢出,但比原来可能性小很多。

  2. 元空间里存放的是类的元数据,能存储多少元数据就不是由JVM里的MaxPermSize(方法区-永生代最大大小)决定了,而是由系统可用内存控制,这样就可以加载更多的数据。

    JDK7及之前:
    –XX PermSize=N //方法区(永生代)初始大小
    –xx MaxPermSize=N //方法区(永生代)最大大小。如果超过这个值,会抛出异常:OutOfMemoryError

    JDK8:元空间
    –XX MetaspaceSize=N //设置Metaspace 的初始(和最小大小)
    –XX MaxMetaspaceSize=N //设置MetaspaceSize的最大大小
    元空间与永生代不同,不指定大小,随着更多类的创建,虚拟机会耗尽所有可用内存空间。

运行时常量池

常量池表会在类加载后存放到方法区的运行时常量池。

常量池表: Class文件除了有类的版本、字段、方法、接口等描述信息外,还有用于存放编译期生成的各种 字面量 和 符号引用 的常量池表。

字面量:源代码里固定值的表示法,即通过字面就可以知道其值得含义。包括:整数、浮点数和字符串字面量

符号引用:类符号引用、字段符号引用、方法符号引用、接口方法符号。

符号引用和直接引用?

符号引用 :以一组符号来描述所引用的目标,符号可以是任意形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定是已经加载到虚拟机内存中的内容。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用字面量形式明确定义在《Java虚拟机规范》的Class文件格式中。

直接引用 :是可以直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是和虚拟机实现的内存布局直接有关,同一符号引用在不同的虚拟机实例翻译出来的直接引用一般是不一样的。如果有直接引用,那引用的目标必定已经在虚拟机的内存中存在。

字符串常量池

是JVM为了提升性能 和减少内存消耗,针对字符串专门开辟的一块区域,主要目的是为了避免字符串的重复创造。

HotSpot虚拟机中字符串常量池的实现是:src/hotspot/share/classfile/stringTable.cpp,StringTable可以理解为一个固定大小的HashTable,容量为StringTableSize(可以通过
-XX:StringTableSize 参数来配置),保存的字符串(key)与字符串对象引用(value)的映射关系,字符串对象的引用指向堆内存中字符串对象。

为什么JDK7要将字符串常量池和静态变量从永生代移动到堆中?

主要是因为永生代(方法区的实现)的GC回收效率太低,只有在整堆收集(Full GC)的时候才会被执行GC。Java程序中通常有大量被创建的字符串等待回收,将字符串常量池放到堆中,能够高效及时的回收字符串内存。

直接内存

是一种特殊内存缓冲区,并不在 Java堆或方法区中分配的,而是通过JNI的方式在本地内存上分配

JNI(Java Native
Interface,Java本地接口),是Java平台中的一个强大特性。应用程序可以通过JNI把C/C++代码集成进Java程序中。JNI是一套双向的接口,允许Java与本地代码间的互操作。

直接内存并不是虚拟机运行时数据区的一部分,也不是Java规范中定义的内存区域,但这部分区域会被频繁使用,而且还会导致OutOfMemoryError错误的出现。

直接内存的分配不会受Java堆的限制,但是会受到本机总内存大小以及处理器寻址空间的限制。

类似的概念:堆外内存。

堆外内存就是把内存对象分配在堆(新生+老年+永久)以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果可以在一定程度上减少垃圾回收对应用程序造成的影响。

文章来源:https://blog.csdn.net/qq_42723240/article/details/135324772
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。