深入理解JVM-JVM内存模型-02

深入理解JVM-JVM内存模型-02

要了解JVM内存模型先来看一张图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以下是JVM虚拟机的构成,本章主要将一些JVM简单的概念与组成(这里一定要区分java内存模型JMM,很多人会混淆JVM和java内存模型)
在这里插入图片描述
本文反编译字节码对应的源代码:

public class Math {
	public static final int initData = 666;
	public static User user = new User() ;
	public int compute() { //一个 方法对应一块栈帧内存区域
		int a=1;
		int b=2;
        int c=(a+b)*10;
		return C;
	public static void main(String[] args){
		Math math = new Math() ;
		math. compute () ;
		System. out. print1n( test" ) ;
	}
}

整个JVM虚拟机包括运行时数据区(JVM内存模型),类装载子系统,字节码执行引擎。
JVM内存模型主要包括五部分
1、堆:JVM最大的内存区域也是大部分对象的存储区域,默认内存上限就是机器内存上限,可设置堆内存大小。
2、栈(线程):栈中有很多栈帧,每个栈帧的结构包括(局部变量表、操作数栈、动态链接、方法出口),可以把每个栈帧内存空间比作一个方法,当一个方法调用另一个方法时,会再建立一个栈帧内存空间,且这个内存空间会在栈顶,栈的先进后出特点刚好符合程序执行规则,后调用的方法最先执行完释放空间。当一个方法无限递归那么栈空间最终会溢出导致StackOverflowError异常

  • 局部变量表:也就是方法内声明的局部变量如a,b,但对于对象来说,上面代码在主线程中有个math变量,存在主线程的局部变量表中的并不是math这个变量,而是new Math()后在堆内存中分配的内存空间地址

  • 操作数栈:在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,利用javap -c *.class 可以将字节码文件反编译成可读的信息,如下图
    在这里插入图片描述
    图中code的含义含义看如下这张图(JVM指令手册)这里只展示了一部分。在这里
    iconst_1将int型常量1压入操作数栈,也就是将整型值1放入操作数栈中;
    istore_1将int类型值存入局部变量1,这里的局部变量1可以理解为在局部变量表中变量所在位置的索引(0位置的索引是this,我们定义的变量一般都是从1开始)将int类型值存入局部变量1也就是把操作数栈中的值赋值给局部变量1。
    iload_1从局部变量1中装载int类型值,就是将局部变量表中的变量1的值放到操作数栈,因为接下来可以要对该值进行操作,比如两个变量相加的情况,比如a+b
    iadd 执行int类型的加法,也就是讲栈中的数值进行相加,比如代码a+b,做完相加之后,这两个数值就要出栈,他们的a+b的结果会压入操作数栈
    bipush将一个8位带符号整数压入栈,在上图中就是把10压入操作数栈
    imul执行int类型的乘法也就是执行上面代码的乘10的操作了,乘10的结果会被压入操作数栈,两个乘数会出栈
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 动态链接:好比一个方法调用另一个方法,为了将调用方法的符号引用转换为调用方法的直接调用(直接调用理解为找到被调用方法的地址代码),可以理解为动态链接就是为了存放被调用的方法地址(代码)

  • 方法出口:也就是方法的返回值返回出去或者发生异常。
    3、本地方法栈:存放一些c++写的本地方法,java中native修饰的方法
    4、程序计数器: 用于存储下一条指令的地址。可以理解为上图反编译文件信息code下的指令前面的数值。
    5、方法区(元空间):存放常量、静态变量、类信息、运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等。

类装载子系统
c++实现,把字节码文件装载进JVM内存
字节码执行引擎
执行JVM内的字节码

在java内存模型中最大的空间是堆,那么对于堆它就有很多的要说的了


在这里插入图片描述

堆内存它有一个分代机制:分为年轻代和老年代
年轻代:主要存放朝生夕死的对象,年轻代占用堆空间的1/3,java存放在堆内存的对象一开始都存放在年轻代,它又分为Eden区和Survivor区,Survivor区分为s0和s1区,当年轻代的Eden区满了则会发生GC(垃圾回收),在年轻代发生垃圾回收一般是Minor GC/Young GC这两者是一样的只是叫法不同,在Eden区满了之后一轮MinorGC后会把幸存的对象放到s0区,如果s0区满了继续GC把存活的对象挪动到s1,且分代年龄+1(分代年龄的增长那么该对象就是长久存在内存中,分代年龄最大为15**,到了15就不得不转移到老年代**)如果达到了设置的分代年龄数值则会把该批对象放到老年代中

以下是年轻代到老年代的转移过程
对象到达15代或者达到设定的分代年龄数该对象则会转移到老年代
在这里插入图片描述

老年代:主要存放经过年轻代多次垃圾回收后还存留的对象,老年代占堆区的2/3,如果老年代内存满了则会发生Major GC/Full GC,FullGC的代价是非常昂贵的,因为FullGC发生时会Stop The World(STW) 也就是会停止主线程的执行而去执行垃圾回收(如果不停止主线程就会导致可能有新的对象产生),在实际生产中我们必须减少FullGC,所以这也是JVM优化的关键
内存可视化工具jvisualvm
jdk自带了一个JVM内存可视化工具jvisualvm,只需在cmd输入jvisualvm就可以打开可视化工具,可以看到虚拟机中内存的情况和发生GC的情况

JVM内存参数设置

在日常开发中,我们基本很少接触这一块,我们在运行java程序时用的都是JVM的默认设置,JVM的内存参数设置如下图:
在这里插入图片描述
JVM内存参数设置SpringBoot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):

java ‐Xms2048MXmx2048MXmn1024MXss512K ‐XX:MetaspaceSize=256M  ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eureka‐server.jar

关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和-XX:MaxMetaspaceSize=N
-XX:MaxMetaspaceSize:设置元空间最大值,默认是-1,即不限制,或者说只受限于本地内存大小。
-XX:MetaspaceSize:指定元空间触发Fullgc的初始阈值(元空间无固定初始大小),以字节为单位,默认是21M,达到该值就会触发fullgc进行类型卸载,同时收集器会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过-XX:MaxMetaspaceSize(如果设置了的话)的情况下,适当提高该值。这个跟早期jdk版本的-XX:PermSize参数意思不一样,
-XX:PermSize代表永久代的初始容量。由于调整元空间的大小需要FullGC,这是非常昂贵的操作,如果应用在启动的时候发生大量FullGC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。

JVM专栏:点我
下一章:深入理解JVM-JVM对象创建与内存分配机制-03-1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值