JVM运行时数据区

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

运行时数据区的组成

  • 运行时数据区由 程序计数器、本地方法栈、java虚拟机栈、堆、方法区 组成

程序计数器

  • 是一块很小的内存空间用来记录每一个线程运行的指令位置是线程私有的每个线程都有一个程序计数器生命周期与线程一致是运行时数据区中唯一一个不会出现内存溢出的空间运行速度最快。

本地方法栈

  • 用来运行本地方法的区域是线程私有的空间大小可以调整可能会出现栈溢出的情况。

Java虚拟机栈

  • 每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应着一次方法的调用。Java 虚拟机栈是线程私有的主管 Java 程序的运行,它保存方法的局部变量(8种基本数据类型,对象的引用地址),部分结果,并参与方法的调用和返回。
  • 栈中会出现异常当线程请求的深度大于虚拟机所允许的深度时会出现StackOverflowError
    在这里插入图片描述
    栈的运行原理
  • JVM直接对java栈的操作只有两个就是对栈帧的入栈和出栈遵循先进后出后进先出的原则。
  • 在一条活动的线程中,一个时间点上,只会有一个活动栈.即只有当前在执行的方法的栈帧(栈顶)是有效地,这个栈帧被称为当前栈帧(Current Frame),与当前栈帧对应的方法称为当前方法(Current Method),定义这个方法的类称为当前类(Current Class)。
  • 执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
  • 如果在该方法中调用了其他方法,对应的新的栈帧就会被创建出来,放在栈的顶端,成为新的当前栈帧。
    在这里插入图片描述
    不同线程中所包含的栈帧方法是不允许存在相互引用的,即不可能在一个栈中 引用另一个线程的栈帧(方法)
    如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧. Java 方法有两种返回的方式,一种是正常的函数返回,使用 return 指令,另一种抛出异常.不管哪种方式,都会导致栈帧被弹出

栈帧的内部结构

  • 一个栈帧包含
    局部变量表存储在方法中声明的变量
    操作数栈实际计算运行
    动态链接
void A(){
   B();//B的方法的地址
}

方法的返回地址

基本作用特征

  • 是存储空间用来存储对象 是内存空间最大的一块儿区域

  • 在jvm启动时就被创建大小可以调整jvm调优

  • 本区域是存在垃圾回收的是线程共享的区域
    堆内存区域划分

  • Java8 及之后堆内存分为:新生区(新生代)+老年区(老年代)

  • 新生区分为 Eden(伊甸园)区和 Survivor(幸存者)区

  • 伊甸园区对象刚刚创建

  • 幸存者1区/2区 : 经历过垃圾回收

  • 老年代较少进行垃圾回收

在这里插入图片描述
为什么要进行分区

  • 可以根据独享存活时间来放在不同的区域可以区别的进行对待频繁的回收年轻代较少的回收老年代。

创建的对象在堆中的分布

  • 新创建的对象在伊甸园区
  • 当发生垃圾回收时将伊甸园中的垃圾对象直接销毁将存活的对象移动到幸存者1区
  • 之后创建的新对象还是存在于伊甸园区再次进行垃圾回收时将伊甸园区中的存活对象一刀幸存者2区同时将在本次扫描中幸存者1区中存活的对象一同存放到幸存者2区每次都确保1个幸存者区为空来进行相互转换
  • 每次垃圾回收时都会记录此对象经历的垃圾回收次数当一个对象经历15次回收仍然存活就会被移动到老年代垃圾回收次数在对象头中有一个4bit的空间记录最大值只能是15
  • 老年代的回收次数较少当内存空间不够用时才会去回收老年代

堆空间的配置比例

  • 默认新生代老年代 = 1 2 可以通过-XX:NewRatio = 2 进行设置
  • 如果项目中生命周期长的对象较多就可以把老年代设置的更大一些
  • 在新生代中伊甸园和两个幸存者区的比例为811可以通过
    -XX:SurvivorRatio = 8 进行设置
  • 对象垃圾回收的年龄-XX:MaxTenuringThreshold=

分代收集的思想

  • JVM 在进行 GC 时,并非每次都新生区和老年区一起回收的,大部分时候回收的都是指新生区.针对 HotSpot VM 的实现,它里面的 GC 按照回收区域又分为两大类型一种是部分收集,一种是整堆收集。
  • 部分收集:不是完整收集整个 java 堆的垃圾收集.其中又分为:
    新生区收集(Minor GC/Yong GC):只是新生区(Eden,S0,S1)的垃圾收集老年区收集(Major GC / Old GC):只是老年区的垃圾收集。
  • 整堆收集(Full GC):收集整个 java 堆和方法区的垃圾收集。
  • 整堆收集出现的情况:
    System.gc();时
    老年区空间不足
    方法区空间不足
    开发期间尽量避免整堆收集。

堆空间参数设置
官网地址 https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

字符串常量池

  • JDK7 及以后的版本中将字符串常量池放到了堆空间中。因为方法区的回收效率很低在 Full GC 的时候才会执行永久代的垃圾回收而 Full GC 是老年代的空间不足、方法区不足时才会触发。
    这就导致字符串常量池回收效率不高而我们开发中会有大量的字符串被创建回收效率低导致永久代内存不足。放到堆里能及时回收内存。
public static void main(String[] args) {
	String temp = "world";
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
				String str = temp + temp;
				temp = str;
				str.intern();//将字符串存储到字符串常量池中
		}
}

方法区

  • 主要用来存储加载类的信息以及及时编译器编译后的信息和运行时常量池
  • 特点在jvm启动时创建大小是可以调整是线程共享也会出现内存溢出
    方法区、堆、栈交互关系
  • 方法区存储类信息元信息
    堆中存储创建的对象
    栈中存储对象的引用
    方法区大小设置
    -XX:MetaSpaceSize 设置方法区的大小
    windows jdk默认的大小是21MB
    也可以设置为-XX:MaxMetaspaceSize 的值为-1级没有限制。没有限制时就可以使用计算机内存
    也可以设置初始值大一点减少 FullGC的发生
    方法区内部结构
  • 方法区它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存,运行常量池等。
  • 运行常量池就是一张表虚拟机指令根据这张表找到要执行的类名、方法名、参数类型、字面量常量等信息,存放编译期间生成的各种字面量常量和符号引用。
    在这里插入图片描述

方法区的垃圾回收

  • 方法区在FullGC时进行垃圾回收
    主要是回收类信息类信息的回收条件比较苛刻满足以下3点
    • 在堆中该类及其子类的对象都不存在了
    • 该类的类加载器不存在了
    • 该类的Class对象不存在了
  • 也可以认为类一旦加载就不会卸载了

特点总结

  • 程序计数器Java栈本地栈是线程私有的
  • 程序计数器不会出现内存溢出
  • java栈本地栈堆方法区可能会出现内存溢出
  • java栈本地栈堆方法区大小是可以调整的
  • 堆方法区是线程共享的是可以进行垃圾回收的
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6