(二)JVM实战——jvm实战之运行时数据区详解

程序计数器

  • 程序计数器是一块非常小的内存区域,它是线程私有的,即每个线程都有自己的程序计数器。在Java虚拟机中,程序计数器用于存储当前线程正在执行的字节码指令的地址或索引,也可以说是当前线程所执行的代码的行号或字节码偏移量。

  • 程序计数器在线程切换时起着重要作用,它记录了每个线程执行的位置。当线程被暂停并再次恢复执行时,虚拟机能够通过程序计数器快速地确定应该从哪里继续执行。对于Java方法,程序计数器存储的是正在执行的字节码指令的地址;对于Native方法(即本地方法),程序计数器的值是undefined。

  • 在多线程环境下,程序计数器为每个线程提供了独立的执行位置记录,因此可以同时执行多个线程而不会相互干扰。

  • 程序计数器不存在OOM,也不存在垃圾回收。

虚拟机栈

  • 栈是线程安全的
  • 栈的数据结构是先进后出,后进先出
  • 栈不存在GC垃圾回收,存在OOM内存溢出(压栈和出栈)
  • 通过设置-Xss参数设置栈内存大小,默认是512k-1M,取决于操作系统。栈的内存设置太大,会导致系统用于创建线程的数量减少,浪费内存空间,导致系统OOM发生。
  • 栈溢出,OOM的异常:StackOverFlowError(固定内存)和OutOfMemoryError(动态内存)
  • 栈管运行,堆管存储,栈的运行效率仅次于程序计数器
  • 栈的单位是栈帧,栈帧由局部变量表操作数栈方法返回地址动态链接一些附加信息组成,一个栈帧对应一个方法
  • 局部变量表:存储方法参数和定义方法体内的局部变量,数据类型包括基础数据类型、对象引用、returnAddress类型

  • 局部变量表所需的容量大小在编译期就确定了

  • 局部变量表中的变量只针对当前方法调用有效,随着方法调用结束,栈帧销毁,局部变量表也会销毁

  • 操作数栈:主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间

  • **动态链接:**在程序执行时动态地将函数调用连接到正确的函数实现。当函数被调用时,动态链接器会确定函数的实际地址,并将调用指令指向该地址,从而实现函数的动态调用。

本地方法栈

  • 本地方法栈是Java虚拟机为执行Java方法而提供的一块内存区域,它用于支持使用本地方法调用(Native Method Invocation)的功能。本地方法栈与虚拟机栈类似,但是它所管理的栈帧是用于执行本地方法(Native Method)的。本地方法是用其他语言(如C或C++)编写的方法,通过Java的本地方法接口(JNI)来调用。

  • 本地方法栈的作用是为Java应用程序提供了与本地(非Java虚拟机实现相关)方法交互的能力。当Java程序调用本地方法时,虚拟机会在本地方法栈中分配一个栈帧,用于执行本地方法。这个栈帧的结构与虚拟机栈中的栈帧类似,但是它包含的是本地方法执行所需的信息,例如参数、局部变量和操作数栈等。

  • 一个jvm实例只存在一个堆内存,在jvm启动的时候被创建,其堆空间也就被创建了,是jvm管理的最大一块内存空间,堆内存大小是可以调节的,物理上是不连续的,逻辑上是连续的。

  • 堆的内存结构

  • jdk8之前:新生区(eden、survivor0、survivor1)、老年区、永久区(方法区)

  • jdk8及之后:新生区(eden、survivor0、survivor1)、老年区、元空间(meta space方法区)

  • 几乎所有的对象都是在Eden区创建出来的;大对象直接在老年区创建;长期存活的对象分配到老年区;

  • 堆内存的参数设置

-Xms(-XX:InitialHeapSize):堆空间初始化大小

-Xmx(-XX:MaxHeapSize):堆空间最大大小,超过堆最大内存,出现OOM

-XX:NewRatio=2:默认老年代与新生代的比例,表示新生代占1,老年代占2,新生代占整个堆内存的1/3

-Xmn:设置新生代的大小

-XX:SuvivorRatio=8:新生代的比例关系,Eden园区占8,survivor0占1,survivor1占1

-XX:HandlePromotionFailure:是 HotSpot虚拟机的一个 JVM 参数,用于控制年轻代对象晋升到老年代的过程中如果老年代空间不足导致晋升失败时的处理方式

  • 继续晋升并进行Full GC:这种方式会尝试继续晋升对象,并在晋升失败后立即进行一次Full GC来释放老年代的空间,以便进行对象晋升。这种处理方式会导致Full GC的频繁发生,对性能有一定影响。

  • 拒绝晋升:这种方式在晋升失败时不进行Full GC,而是拒绝晋升对象,使其继续留在年轻代。当发生晋升失败时,会先尝试进行一次Minor GC来释放年轻代的空间,如果仍然无法满足晋升要求,则会通过分配担保机制来直接将晋升失败的对象分配到老年代,避免了Full GC的发生。这种方式可以减少Full GC的频率,但会增加老年代的内存压力。

  • true:继续晋升并进行Full GC。

  • false:拒绝晋升,不进行Full GC。

  • 默认值:与 -XX:+HandlePromotionFailure 相同,默认值为 false

-XX:MaxTenuringThreshold=<N>:设置对象晋升老年代的阈值,默认是15

-XX:+PrintGCDetails

  • -Xms和-Xmx参数通常会设置为一样大小,这是因为在垃圾回收清理完堆内存后不需要重新分隔计算堆内存的大小,从而提高jvm性能

  • 最大内存默认是物理内存的1/4,最小内存默认是物理内存的1/64(大于1G)即为16M

  • 堆内存回收:频繁在新生代回收(朝生夕死)、很少在老年代回收、几乎不在永久区和元空间收集

  • GC垃圾回收的分类

  • MinorGC:新生代的垃圾回收,Eden区满了会触发垃圾回收,Survivor区满了不会触发垃圾回收,回收速度比较快

  • MajorGC:老年代的垃圾回收,只有cms垃圾回收器支持老年代的回收;老年代空间不足,先尝试触发MinorGC,如果之后空间还不足,则触发MajorGC,回收后内存还不足,就报OOM

  • FullGC:方法区和整个堆区的垃圾回收;老年代和方法区空间不足触发垃圾回收或者大对象进入老年代,空间不足;通过MinorGC 进入老年代的对象平均大小大于老年代的可用内存;

  • TLAB:是Eden区线程安全的内存区域,默认占整个Eden园区的1%;会优先作为内存分配的首选;

方法区

  • jdk8之前在永久代,jdk8及之后在元空间

  • 参数设置

-XX:PermSize:初始永久代大小设置,默认21M

-XX:MaxPermSize:最大永久代大小设置,默认82M

-XX:MetaspaceSzie:初始元空间大小设置,默认21M

-XX:MaxMetaspaceSzie:最大元空间大小设置,没有限制,使用本地内存

  • 静态变量和字符串常量在jdk7以后移入堆中,不再存放在方法区
相关推荐
程序猿20232 小时前
MAT(memory analyzer tool)主要功能
jvm
期待のcode5 小时前
Java虚拟机的非堆内存
java·开发语言·jvm
jmxwzy9 小时前
JVM(java虚拟机)
jvm
Maỿbe9 小时前
JVM中的类加载&&Minor GC与Full GC
jvm
人道领域10 小时前
【零基础学java】(等待唤醒机制,线程池补充)
java·开发语言·jvm
小突突突10 小时前
浅谈JVM
jvm
饺子大魔王的男人12 小时前
远程调试总碰壁?局域网成 “绊脚石”?Remote JVM Debug与cpolar的合作让效率飙升
网络·jvm
天“码”行空1 天前
java面向对象的三大特性之一多态
java·开发语言·jvm
独自破碎E1 天前
JVM的内存区域是怎么划分的?
jvm
期待のcode1 天前
认识Java虚拟机
java·开发语言·jvm