主要记录JVM里面的运行时数据区的知识,包括程序计数器,Java虚拟机栈,本地方方法栈,运行时方法区,堆相关知识;Java虚拟机栈中的栈帧介绍,堆中分区划代,各个分区如何工作,垃圾回收机制在各个分区如何工作
文章目录
知识图谱:
一、运行时数据区
主要处理Java程序在运行的时候产生的数据,并进行处理
主要包括:
- 程序计数器
- Java虚拟机栈
- 本地方法栈
- 堆
- 运行时方法区
1.程序计数器
是Java中占用很小的一块内存区间,也是不会发生内存溢出的区间。主要是用来记录线程将要执行指令的位置,是线程独立的,生命周期与线程保持一致
2.Java虚拟机栈
Java虚拟机栈是一个运行时单位,负责Java程序在执行方法时,记录执行的方法信息,当有一个方法执行的时候,就会发生压栈操作,存储该方法信息的地方称之为栈帧,当方法执行完毕后,就会出栈,是线程独立的
栈帧的内部结构:
- 局部变量表
是一组变量值存储空间,用于存放方法参数与方法内部定义的局部变量。对于基本数据类型的变量,则直接存储它的值,对于引用数据类型,则存储指向对象的引用 - 操作数栈
栈最典型的一个应用是对表达式求值。在一个线程执行方法的过程中,实际上就是不断执行语句的过程,归根结底就是进行计算的过程,所以可以这样子说:计算机所有的计算过程都是由操作数栈实现的 - 动态链接
指向运行时常量池的方法引用,方法的调用地址。方法在运行的时候,会用到类中的常量(为类中属性最开始定义的数据),所以必须要有一个引用指向运行时常量 - 方法返回地址
当一个方法执行完毕之后,需要返回到调用它的地方,因此必须在栈帧中保存一个方法返回地址
3.本地方法栈
在Java中使用Native修饰的方法就是本地方法,也就是操作系统的方法,如果调用了本地方法,则把这些本地方法放在本地方法栈中运行,是线程独立的;内存的空间大小可以调整;可能会出现栈溢出
4.堆
堆是一块用于存储JVM里面对象的内存空间,物理上不连续,逻辑上连续;是内存管理的核心区域,也是JVM里面管理的最大一块空间,是垃圾回收的重点区域,堆空间是线程共享的
4.1 分区目的
分区的目的是便于垃圾回收,因为对象的存活时间各不相同,当我们进行垃圾回收的时候,应该选择不同的算法回收对象垃圾,实现比较优的垃圾回收效果
堆的分区:分为新生代与老年代
新生代区又分为:
- 伊甸园区
用于存储新创建的对象 - 幸存者区
用于存储每次经过垃圾回收之后伊甸园区没有被回收的对象;幸存者区域分为两块,一块是s0区域,一块是s1区域,也可以称之为from区与to区
4.2 垃圾回收在堆中的运行过程
假设现在的新生代与老年代都是空的,我们此时创建一个对象,堆会在伊甸园区存储这个对象,当发生垃圾回收之后,如果这个对象没有被回收,伊甸园区的这个对象会被放到幸存者区域s0,并且会在这个对象的对象头区域(分配了4bit)存储它的移动次数,此时会将移动次数变为1;下一次发生垃圾回收之后,如果这个对象没有被回收,则会将它从s0区域移动到s1区域。这个过程中,始终保持着幸存者区域的两个分区有一个分区保持空白,并且伊甸园区是始终存储着新创建对象信息
在刚才的假设上发生点变化,我们一共创建两个对象。第一个对象现在已经被存储在了s0区域,此时创建第二个对象,那么它的变化会是怎样的?当第二个对象被创建后,会被存储在伊甸园区,发生垃圾回收之后,是垃圾的对象,会被移出内存,不是垃圾,则会将这个对象移到s1区域,但是此时s0区域还有一个对象,我们还要保证s0与s1存在一个空白区域,则此时,会检测s0区域的对象,是垃圾则回收,不是垃圾则将它从s0区域移入到s1区域。每发生一次移动都会将对象头信息进行修改,如果移动次数成了15,则将幸存者区域的对象存储到老年代区域
4.3 分区大小
新生代与老年代默认的比率是1:2;-XX:NewRatio=2
,表示新生代占1,老年代占2,新生代占整个堆空间的1/3;如果生命周期长的对象较多时,可以调整这个参数进行调优
伊甸园区存储着新生的对象,这块区域较大;幸存者区域不停地发生着清空与移动,所以这块区域内存大小较小
伊甸园区与两个幸存者区默认是8:1:1;-XX:SurvivorRatio=8
,表示伊甸园区占新生代区域的8/10
在默认参数下对象经过15次垃圾回收依然存活会去老年代;参数最大值是15
4.4 分代收集思想:
JVM中的垃圾回收可以根据不同的区域进行回收,主要包括Minor GC,Major GC,Full GC;
JVM在进行垃圾回收的时候,分为部分收集与整堆收集;部分收集主要针对的是新生代与老师代的堆空间,整堆收集朱要针对整个方法区与堆
部分收集: 分别对堆空间里面的新生代区域与老年代区域进行垃圾回收
- 新生代收集(Minor GC/Yong GC):主要收集伊甸园区与幸存者区垃圾
- 老年代收集(Major GC/Old GC):主要收集老年代区垃圾
整堆收集:(Full GC):收集整个Java堆与方法区垃圾
整堆收集出现的情况:
- System.gc()时
- 老年区空间不足
- 方法区空间不足
应该尽量避免出现整堆收集
字符串常量池在JDK1.7之后的位置由方法区转移到了堆中,因为方法区垃圾回收只会在Full GC的时候才会被回收,效率较低
4.5 堆空间参数设置
官网地址:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
-XX:+PrintFlagsInitial 查看所有参数的默认初始值
-XX:+PrintFlagsFinal 查看所有参数的最终值(修改后的值)
-Xms:初始堆空间内存(默认为物理内存的 1/64)
-Xmx:最大堆空间内存(默认为物理内存的 1/4)
-Xmn:设置新生代的大小(初始值及最大值)
-XX:NewRatio:配置新生代与老年代在堆结构的占比
-XX:SurvivorRatio:设置新生代中 Eden 和 S0/S1 空间比例
-XX:MaxTenuringTreshold:设置新生代垃圾的最大年龄
-XX:+PrintGCDetails 输出详细的 GC 处理日志
5.运行时方法区
主要存储类信息,如静态常量,静态变量,方法属性;运行时常量池
运行时常量池主要记录类中的常量数值信息(字面量值,符号引用)
方法区是线程共享,也可能出现内存溢出,也会涉及垃圾回收
方法区在JVM启动后就会被创建,方法区的大小决定了系统中可以保存多少类
方法区默认的大小是21M,当内存大小达到21M后,会触发FUll GC,进行回收内存,方法区大小可以设置为 -1,表示内存大小无限制
方法区垃圾回收主要涉及:静态常量以及类信息
类信息回收要满足3个条件
- 该类的子类都已经被回收
- 改类的java.lang.Class对象已经不被其他地方引用
- 改类的类加载器已经被回收