JVM体系结构概述

JVM体系结构概述

在这里插入图片描述

一、类加载器ClassLoader

相当于一个快递员,用来将物理硬盘中的class文件加载进JVM(中的方法区),JAVA自带的类加载器有三个(除了自带的,用户也可以集成ClassLoad抽象类自定义加载器)

1、启动类加载器(Bootstrap)

加载Jdk自带的类时(jre/lib/rt.jar包下的类,常用:Object、String、ArrayList)使用启动类加载器,使用C++编写;

2、扩展类加载器(Extension)

加载扩展包中的类(jre/lib/ext/*.jar),使用扩展类加载器;

3、应用程序类加载器(AppClassLoader)

加载自定义的类,使用应用程序类加载器;

双亲委派机制、沙箱安全机制

当一个类加载器收到类加载请求,它首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,父类再调用自己的父类去完成,每一个层次的类加载器都如此,因此所有的加载请求都会先传送到启动类加载器中,只有父类加载器反馈自己无法完成这个请求的时候(在它加载的路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载(在自己加载不成功时,再继续调子类,最终的子类如果还是加载不成功,则报ClassNotFindException)。

采用双亲委派的作用就是保证了沙箱安全、保证jre运行环境安全,不会被自定义的类所污染,例如自己手写一个java.lang.String类,但是在类加载的过程中,加载的还是rt.jar包中的String对象。

口说无凭,看图:
在这里插入图片描述

二、执行引擎 Execution Engine

执行引擎负责解释命令,提交操作系统执行。

三、本地方法接口 | 本地方法栈

本地方法:本地方法就是native关键字修饰的方法,因为方法是c语言描述的,所以方法体中没有。例如Thread类中的start()方法中的start0()方法。

本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,因为JAVA诞生的时候是C/C++横行的时候,想要立足必须有调用C/C++的程序,于是就在内存中开辟了一块区域处理标记为natative的代码。 它的具体做法是在本地方法栈(Native Method Stack)中登记native方法,在执行引擎(Execution Engine)执行的时候加载本地方法库(native libraies)。

目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域很发达,比如可以使用Socket通信,也可以使用Web Service等等。

在这里插入图片描述

四、程序计数器(PC寄存器)

程序计数器线程独有的, 可以把它看作是当前线程执行的字节码的行号指示器,比如如下字节码内容,在每个字节码`前面都有一个数字(行号),我们可以认为它就是程序计数器存储的内容。

那么记录这些数字(指令地址)有啥用呢,我们知道 Java 虚拟机的多线程是通过线程轮流切换并分配处理器的时间来完成的,在任何一个时刻,一个处理器只会执行一个线程,如果这个线程被分配的时间片执行完了(线程被挂起),处理器会切换到另外一个线程执行,当下次轮到执行被挂起的线程(唤醒线程)时,怎么知道上次执行到哪了呢,通过记录在程序计数器中的行号指示器即可知道,所以程序计数器的主要作用是记录线程运行时的状态,方便线程被唤醒时能从上一次被挂起时的状态继续执行,需要注意的是,程序计数器是唯一一个在 Java 虚拟机规范中没有规定任何 OOM 情况的区域,所以这块区域不需要进行 GC。

在这里插入图片描述

五、方法区

供各线程共享的运行时内存区域。它存储了每一个类的结构信息,例如运行时常量池、字段和方法数据、构造函数和普通方法。它起到一个“模板”的作用,用于在堆中new一个对象的时候,根据方法区中的类来new。

方法区是一个规范,不同的虚拟机的实现是不一样的,最典型的就是永久代(jdk7)和元空间(jdk8)。永久代和元空间的区别在于:永久代存在于堆当中,元空间在本地内存当中。他们都不会发生垃圾回收。

六、栈

“栈管运行,堆管存储”。栈,主管Java程序的运行,是在线程创建的时候创建,它的生命周期是跟随线程的生命周期,线程结束栈内存也就释放,对于栈来说,不存在垃圾回收问题,生命周期和线程一致,是线程私有的。栈中保存8中基本类型的变量+对象的引用变量+实例方法。

每一个Java方法存在于栈中就是一个栈帧,那么栈帧中保存的又是什么呢?
局部变量表、操作数栈、动态链接、方法出口等信息。

在方法调用深度太长后,会将栈撑爆,会报StackOverFlowError错误。

七、堆

堆分成新生代和老生代(Java8以前还有个永久代),默认比例为 1 : 2,新生代又分为 Eden 区, from Survivor 区(简称S0),to Survivor 区(简称 S1),三者的比例为 8: 1 : 1,这样就可以根据新老生代的特点选择最合适的垃圾回收算法,我们把新生代发生的 GC 称为 Young GC(也叫 Minor GC),老年代发生的 GC 称为 Old GC(也称为 Full GC)。

堆是JVM中最重要的区域,主要涉及到堆的垃圾回收机制。都给你准备好啦,戳这里:https://blog.csdn.net/weixin_44236420/article/details/104811896

栈、堆、方法区 三者关系:
举例说明:Student s = new Student();
在栈中新建一个s对象,它指向堆当中新开辟的内存空间,该内存空间存放的是根据方法区中的Student.Class作为模板创建的对象。

JVM参数调优

JVM参数分类:

  • 标配参数(了解)
  • X参数(了解)
  • XX参数(掌握)

标配参数

指的是在jdk各个版本之间很稳定,很少有大变化,例如:java -version;java -help;java -showversion。

X参数

指的是jdk解析class文件执行方式,有3种类型可选,-Xint【解释执行】、-Xcomp【第一次使用就编译成本地代码】、-Xmixed【混合模式】
在这里插入图片描述

XX参数

1、Bolean类型

用于给JVM 开启/关闭 某属性。
使用方式:-XX:(+/-)[某属性名]
在这里插入图片描述

验证是否开启成功:在程序运行的情况下,使用 jps -l 命令查看当前运行的线程及端口号,再使用 jinfo -flag [属性名称][端口号],判断该进程是否开启某属性
在这里插入图片描述

返回结果
-XX:+ [属性名] 表示该属性已开启
-XX:- [属性名] 表示该属性已关闭

2、KV设值类型

用于给某属性设置某值
使用方式:-XX:属性key=属性值value
在这里插入图片描述
验证是否设置成功:在程序运行的情况下,使用 jps -l 命令查看当前运行的线程及端口号,再使用 jinfo -flag [属性名称][端口号],查询该进程某属性值
在这里插入图片描述

-Xms、-Xmx属于什么类型?

两者也是KV设值类型

-Xms=-XX:InitialHeapSize;
-Xmx=-XX:MaxHeapSize
-Xms: 堆的初始内存大小,默认为物理容量的1/64-Xmx: 堆最大内存大小,默认为物理容量的1/4-XX:+PrintGCDetails: 打印堆内GC日志,用来查看堆运行状态;
-XX:MaxTenuringThreshold:设置对象在新生代中的移动次数(默认15,达到后进老年代)

其他查看命令

java -XX:+PrintFlagsInitial//查看当前JVM所有初始值
java -XX:+PrintFlagsFinal//查看当前JVM所有修改后(最终)的值

在这里插入图片描述
在这里插入图片描述

java -XX:+PrintCommandLineFlags -version//打印命令行参数,主要用于查看使用的垃圾收集器

在这里插入图片描述

常用修改参数

-Xms4096m =-XX:InitialHeapSize;//设置初始堆大小(默认物理堆1/64)
-Xmx4096m =-XX:MaxHeapSize;//设置最大堆大小(默认物理堆1/4)
-Xss1024k =-XX:ThreadStackSize;//设置单个线程栈大小(linux默认1024k)
-XX:MetaspaceSize=20m;//设置方法区[永久代、元空间]大小(默认20M)
-XX:+PrintGCDetails;//设置 打印GC日志
-XX:MaxDirectMemorySize=10m//设置堆外内存大小
-Xmn; //设置新生代大小(默认Java堆的1/3),这个属性一般不改(如果与-XX:NewRatio属性值冲突,使用的是-Xmn的值)
-XX:SurvivorRatio=8//设置eden区的新生代占比(默认为8,即eden:S0:S1=8:1:1),这个值一般不改
-XX:NewRatio=2//设置老生代的堆空间占比(默认为2,即 新生代:老年代=1:2),一般默认
-XX:MaxTenuringThreshold=15//设置垃圾年龄,多少岁之后从新生代进入老年代(默认15,自定义时不能大于15)

设置案例:

-Xms4096m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandFlags -XX:+PrintGCDetails -XX:+UseSerialGC
Runtime.getRuntime().availableProcessors();//处理器个数
Runtime.getRuntime().maxMemory();//JVM最大内存
Runtime.getRuntime().totalMemory();//JVM初始内存
System.gc();//手动GC,一般不要用,系统会自动GC

正式环境调内存要求:内存初始值和内存最大值尽量保持相同,避免GC争抢内存造成内存忽高忽低。

GC日志解读:

//GC类型
[GC
//YGC 前新生代内存->YGC 后新生代内存(新生代总内存)
	[PSYoungGen: 0K->0K(3008K)] 
//YGC 前JVM堆内存占用->YGC 后JVM堆内存占用(JVM堆总大小),YGC耗时
	735K->735K(9856K), 0.0003054 secs]
//YGC用户耗时        YGC系统耗时  YGC实绩耗时
	[Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC 
	[PSYoungGen: 0K->0K(3008K)]
	[PSOldGen: 735K->627K(6848K)] 735K->627K(9856K) 
	[PSPermGen: 3550K->3541K(21248K)], 0.0111451 secs] 
	[Times: user=0.02 sys=0.00, real=0.01 secs] 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值