JAVA虚拟机详解

JAVA虚拟机详解

1.前言

java虚拟机有自己完善的硬件架构。如处理器,堆,栈,寄存器,指令系统。JVM的主要工作是解释自己的指令集(即字节码文件)并映射到本地的CPU指令集或OS系统的调用。

2.虚拟机整体结构

在这里插入图片描述

3.JVM虚拟机各部分详解

3.1 --装载器子系统

  • 装载器的作用:

从入口处按需装载.class文件到运行时数据区域,执行数据的初始化工作,供执行引擎执行。

  • 装载器的分类:

装载器一般分为两大类:启动类装载器,用户自定义装载器
启动类装载器又分为:引导类装载器,拓展类装载器,系统类装载器

在这里插入图片描述
注意: 将自定义的类放到lib文件夹下,该类的加载由扩展类加载器来加载。出于安全考虑,引导类加载器不会加载陌生的类。

  • 装载器的装载流程

装载器的装载流程可以分为大体上分为三个阶段。详细可分为五个阶段。
大体可分为:加载——链接——初始化
详细可分为:加载——检验——准备——解析——初始化

在这里插入图片描述

3.2 --运行时数据区域

  • 运行时数据区域的分类

运行时数据区域可分为:方法区,堆区,java栈区,本地方法栈,程序计数器。

  • 运行时数据区域

1.每运行一个JVM实例,系统都会分配一块内存空间用来存储数据,由JVM自行管理。
2.在运行时数据区域中,方法区和堆区是JVM实例中所有线程共享的。java栈,本地方法栈,程序计数器都是线程私有的。随着线程的创建而创建,随着线程的结束而消亡。
3.在运行时数据区域中。程序计数器是唯一不会造成内存溢出的内存区域。
4.在运行时数据区域中,堆区是垃圾回收机制的主要活动区域。

3.2.1 ——程序计数器(独立互不影响)

程序计数器也可以叫做行号指示器,是线程私有的,线程创建时创建,线程结束时消亡。用来存储当前正在执行的字节码的指令地址或偏移量,可以持有下一行字节码指令的地址。当访问本地方法(native)时,程序计数器的值为undefined,不存储任何信息。

  • 将程序计数器设置为线程私有的原因

     程序计数器是一块内存区域,用来记录线程当前要执行的指令地址。线程是占用CPU执行的基本单位。而CPU一般是使用时间片轮询方式让线程轮询占用的,所有当前线程CPU时间片用完之后,就需要让出CPU,等下次轮到自己再执行。所以设置为线程私有,就是为了记录线程让出CPU时间时执行的指令地址。等到再次获得CPU时间片就能够按照记录的指令地址继续执行。
    
3.2.2 ——java栈

java栈也是线程私有的,在线程启动时创建,JVM只能进行压栈,弹栈操作。在访问方法时进行压栈操作,方法结束时进行弹栈操作。超出栈内存会抛出栈内存溢出错误。

  • JAVA栈的划分

java栈可细化为:本地变量数组,操作栈帧,方法所属类的常量池引用
java栈也可细化为:局部变量数组,操作栈帧,动态链接,方法出口。
两个划分本地操作相同,只是定义不同。

划分方式一:
在这里插入图片描述

划分方式二:
在这里插入图片描述

3.2.3 —— 本地方法栈
  • 本地方法栈

1.通过JavaNativeInterface(JNI)本地方法接口调用本地方法(其他编程语言编写的代码),根据每个方法语言类型会创建相应的栈,用于存储每个本地方法的调用状况。基本情况与JAVA栈类似。
2.本地方法栈是线程私有的。

3.2.4 —— JAVA堆
  • 堆区(对JVM性能影响最大)

1.在虚拟机启动时创建,内存占用最大,是JVM实例中所有线程共享的内存区域,内存空间不连续,运行速度最慢,需要GC回收机制
2.只要通过new创建的对象内存都在堆中分配。堆空间溢出会导致程序崩溃,所以当对象执行结束时,其占据的内存空间需要等待GC进行回收

  • 什么时候堆中的元素会被作为垃圾元素等待回收?

     当堆中元素在栈中没有变量指向时,会被认为是垃圾等待GC回收。
    
  • 如何判断是否回收(个人理解)

1.引用计数法(缺点:无法解决循环引用问题)
当堆中元素被使用时,计数器加1,结束使用,计数器减1。当计数器最后为0时,表示队中元素没有被使用,能被进行回收。
2.可达性分析法
当元素的使用通过节点形式进行连接。在判断节点是否被回收时,通过查找节点分支,当节点分支无任何链接,表示可以被回收

  • JAVA堆的划分
    在这里插入图片描述
  • JAVA堆中内存使用详解

创建对象,首先会存入Eden区和s1区(From Survivor区域),当Eden满了,就会启动minor gc 回收不再使用的对象,将Eden和s1区中存活的对象放到s2区(To Survivor区域),剩下进行清除。s1和s2交换角色。如果经过多次清除还保留的元素会被放到老年代中。当Eden,s1,s2区都被占满后,就会将创建的对象放到老年代中。老年代占满之后就会触发FullGC回收堆,方法区对象。

  • 堆中垃圾回收算法的介绍

1.复制算法(缺点:内存使用率降低)
复制算法:将内存空间分为两份,每次只占用其中一份,当空间被占满之后,就会将该空间所有存活的对象放到另一半的空间中,将原来那一半空间对象进行清除。
2.标记清除(缺点:会产生很多碎片空间)
标记清除:将需要回收的对象进行标记,当空间被占满后,清除标记的对象。
3.标记整理
标记整理:将需要回收的对象进行标记,并向一边移动,当空间占满后,清除标记的对象。

  • 堆中内存泄漏场景

内存泄漏:不断有新增对象长期不被回收,导致老年代挤压,全量线程阻塞。
场景
1.类静态属性
2.未关闭资源。流,数据库连接
3.Threadlocal,每个线程中都会有Threadlocal变量副本,线程不结束,不清除。尤其是线程池
的情况。

3.2.5 ——方法区
  • 方法区

1.和堆一样,是所有线程共享的,1.8之前称为永久代,1.8之后称为元空间,内部逻辑也发生改变。二者最大的区别就是,永久代占用的是JVM的内存空间,而元空间占用的是本地内存空间。
2.主要用来存放已被虚拟机加载的类型信息(包括类,接口,枚举,域,方法等),常量,静态资源,即使编译器编译后的代码缓存等。
3.内存空间不连续,和堆一样,可选择固定大小或扩展。
4.方法区垃圾回收主要是两部分内容:【常量池中不使用的常量】【类的卸载】

  • 方法区内存分析
    在这里插入图片描述

3.3 执行引擎(解释器)

执行引擎(将字节码文件转换成机器码文件)

  • 第一代解释器

    JVM一条一条读取,解释并执行字节码指令,解释执行,解释快,执行慢。
    
  • 第二代解释器(即时编译器(JIT编译))

    代码第一次执行被编译之后,存入缓存中。第二次使用,直接从缓存中拿。
    
  • 第三代解释器(自适应编译器)

    代码执行时机比JIT编译迟,先将程序以某种方式运行起来,收集信息进行动态编译,在代码执行过程中监控代码执行频率,缓存利用率高的代码。

  • HotSpot(混合模式的执行引擎)-【解释器,自适应编译器】

    将需要编译的热点代码编译到本地代码,发现不频繁使用时,将本地代码从缓存中移除,重新进行解释执行。

4.JVM虚拟机运行命令

1.-Xms:设置堆空间最小内存
2.-Xmx:设置堆空间最大内存
3.-XX:NewSize,设置新生代最小大小
4.-XX:MaxNewSize,设置新生代最大空间代销
5.-XX:PermSize,设置方法区最小空间
6.-XX:MaxPermSize,设置方法区最大空间大小
7.-Xss:栈大小
8.Jps:查看进程相关信息
9.Jstat:JVM运行时状态信息,内存信息,垃圾状态
10.JInfo:JVM参数
11.JStack:线程快照,可定位死锁,死循环。
12.top:动态查看当前占用最多的CPU进程
13.top -Hp 进程ID :进程CPU使用率占用最高CPU进程ID。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值