JVM笔记前篇:JVM体系+类加载子系统+运行时数据区(栈、堆、GC)

这里写目录标题

一、JVM与Java体系结构

1.1 JVM的整体结构

在这里插入图片描述

1.2 JVM的架构模型

1. 基于栈的指令集架构

  • 设计和实现更简单,适用于资源受限的系统:
  • 避开了寄存器的分配难题: 使用零地址指令方式分配。
  • 指令流中的指令大部分是零地址指令,其执行过程依赖于操作栈。指令集更小,编译器容易实现。
  • 不需要硬件支持,可移植性更好,更好实现跨平台

2. 基于寄存器的指令集架构

  • 典型的应用是x86的二进制指令集: 比如传统的PC以及Android的Davlik虚拟机。
  • 指令集架构则完全依赖硬件,可移植性差
  • 性能优秀和执行更高效:
  • 花费更少的指令去完成一项操作。
  • 在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主。

1.3 JVM的生命周期

  1. 启动:由引导类加载器bootstrap class loader加载初始类(init class)完成的,这个类是由虚拟机具体制定的
  2. 执行:执行主要是执行java指令,java指令执行完了虚拟机也就结束了
  3. 结束:虚拟机自己结束、异常结束、System.exit(0)结束

1.4 JVM发展历程

1. Sun Classic VM

在这里插入图片描述
执行引擎:

  • 解释器:逐行解释字节码,类似步行,一直在运行不卡顿但是比较慢
  • JIT:翻译本地机器指令,翻译完后速度快。类似公交车,刚开始要等,后面很快

2.Exact VM

在这里插入图片描述

3. HotSpot VM(重点:三大虚拟机3,4,5)

在这里插入图片描述

4. JRockit

在这里插入图片描述

5. IBM 的 J9

在这里插入图片描述

6. KVM、CDC、CLDC的介绍

在这里插入图片描述

7. Azul VM 和Liquid VM

特点:针对特定硬件做了优化,耦合度高,所以性能特别好,但是应用场景有限

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

8. Apache Harmony

在这里插入图片描述

9. Microsoft JVM 和 TaobaoJVM

在这里插入图片描述

在这里插入图片描述

10.Dalvik VM

在这里插入图片描述
具体JVM的内存结构,其实取决于其实现,不同厂商的JVM,或者同一厂商发布的不同版本,都有可能存在一定差异。本套课程主要以oracle HotSpot VM为默认虚拟机。

11. Graal VM

在这里插入图片描述

二、类加载子系统

先看一下内存图
在这里插入图片描述
复杂版:
在这里插入图片描述

1.作用

在这里插入图片描述
在这里插入图片描述
类加载过程:
在这里插入图片描述
在这里插入图片描述

1. 类的加载过程一:加载Loading

  1. 通过一个类的全限定名获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的访问入口

方法区:1.7以前叫永久代 1.8开始叫元空间 (都是方法区的实现

2. 类的加载过程二:链接

在这里插入图片描述

3. 类的加载过程三:初始化

在这里插入图片描述

2. 类加载器的分类

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

1. Bootstrap ClassLoader

启动类(引导类加载器
在这里插入图片描述

2. Extension ClassLoader

在这里插入图片描述

3. AppClassLoader

在这里插入图片描述

用户自定义类加载器

  • 在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定制类的加载方式。
  • 为什么要自定义类加载器?
    – 隔离加载类
    – 修改类加载的方式
    – 扩展加载源
    – 防止源码泄漏

p33

ClassLoader

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

3.双亲委派机制

小总结:在java中存在很多的加载器(class
loader),当一个类加载器收到了加载请求,他会先把请求委托给父类去执行,如果父类还有父类就一直委托,然后从最高父类往下,谁要加载谁加载

Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由父类处理I它是一种任务委派模式。

在这里插入图片描述

在这里插入图片描述

4. 双亲委派机制优势

在这里插入图片描述

5. 沙箱安全机制

自定义string类,但是在加载自定义string类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载idk自带的文件(rt.jar包中java
lang\string.class),报错信息说没有main方法就是因为加载的是rt.jar包中的string类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制

6. 其他

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

三、运行时数据区概述及线程

在这里插入图片描述
内存是非常重要的系统资源,是硬盘和CPU 的中间仓库及桥梁承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策略,保证了JVM的高效稳定运行。不同的JVM对于内存的划分方式和管理机制存在着部分差异。结合JVM虚拟机规范,来探讨一下经典的JVM内存布局。

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

2. 线程

在这里插入图片描述
守护线程、普通线程
在这里插入图片描述

3. 程序计数器(PC寄存器)

1.介绍

只有PC寄存器没有OOM

  1. 介绍在这里插入图片描述
  2. 作用:在这里插入图片描述
  3. 因为只是一个地址所以很小在这里插入图片描述
  4. 在这里插入图片描述

2. 举例说明

在这里插入图片描述

3. 常见面试题

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

4. 虚拟机栈

重要:栈、堆、方法区

1. 概述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

2. 虚拟机常见异常

在这里插入图片描述
321312

3. 栈的存储单位

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

4. 栈帧

在这里插入图片描述

4.1. 局部变量表(local variables)

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

4.1.1. 变量槽slot的理解与演示

省流:float、double占两个slot槽
在这里插入图片描述

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

4.1.2. 静态变量与局部变量对比的小结

局部变量:在使用前,必须要进行显式赋值!否则编译不通过
在这里插入图片描述
在这里插入图片描述

4.2. 操作数栈(Operand Stack)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码追踪:
在这里插入图片描述

4.2 动态链接

指向运行时常量池的引用
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.3 方法的调用

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

4.2 虚方法与非虚方法

省流:能被重写的都是虚方法,只有不能被重写的才是非虚方法
在这里插入图片描述
在这里插入图片描述

4.3 invokedynamic指令的使用

在这里插入图片描述
动态类型:js、python
静态类型:java

省流:使用lambda表达式会出现invokedynamic

4.4 方法重写的本质与虚方法表的试用

省流:
为什么要建立虚方法表?
因为在面向对象的编程中,会很频繁的使用到动态分派,每次加载某个方法都要先找他本身,然后再找他的直接父类,父类没有再网上一直找,很影响效率,因此为了提高性能,在方法区建立了虚方法表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
省流:重写过的方法,在虚方法表中指向自己,没重写过的直接指向父类的方法,就不用一层一层往上找了

4.5 方法返回地址

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

4.6 一些附加信息

在这里插入图片描述

4.7 面试题+总结

在这里插入图片描述

5. 本地方法接口

本地方法是有方法体的,只不过方法体是c/c++实现的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6. 本地方法栈

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

7. 堆(重要)

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

1. 堆的核心概述

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

2. 堆的细分内存结构

在这里插入图片描述

3. 设置堆内存大小与OOM


在这里插入图片描述

4. OOM的举例说明

在这里插入图片描述

5. 年轻代与老年代

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

6. 图解对象分配过程

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

省流:

  • 年龄计数器:每移动一次年龄计数器+1,到15(默认值,可设置)的时候移动到老年代,到老年代以后年龄计数器就没用了
  • 幸存者s0,s1,from,to:复制之后有交换,谁空谁是to,每次Eden过来的移动到to区
  • 触发YGC/Minor GC的时机:Eden区满,对Eden区和幸存者区同时进行YGC的判断,存活下来的年龄计数器+1
  • 垃圾收集规律:频繁在新生区收集,很少在养老区收集,几乎不在永久区元空间收集

7. 对象分配的特殊情况

在这里插入图片描述
代码演示对象分配过程:
在这里插入图片描述
在这里插入图片描述

8. 常用调优工具

在这里插入图片描述

9. Minor GC、Major GC与Full GC

调优:希望GC情况少一些,因为GC的时候会导致用户进程(执行程序的进程)的暂停(STW)

  • Minor GC:

在这里插入图片描述

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

在这里插入图片描述

10. GC举例与日志分析

流程:先往新生代中的Eden区添加数据,等到Eden区满以后进行Young GC/Minor GC,将结果放到s0、s1区,等到年龄计数器等于15,放到老年代中,如果老年代满触发full gc

-Xms9m -Xmx9m -XX:+PrintGCDetails
[GC (Allocation Failure) [PSYoungGen: 2039K->480K(2560K)] 2039K->764K(9728K), 0.0031412 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2220K->512K(2560K)] 2504K->1188K(9728K), 0.0012581 secs] [Times: user=0.00 sys=0.02, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2470K->496K(2560K)] 3146K->1940K(9728K), 0.0015030 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1302K->496K(2560K)] 7354K->6564K(9728K), 0.0012464 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 496K->480K(2560K)] 6564K->6564K(9728K), 0.0025705 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 6084K->3712K(7168K)] 6564K->3712K(9728K), [Metaspace: 3295K->3295K(1056768K)], 0.0088732 secs] [Times: user=0.00 sys=0.02, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 38K->32K(1536K)] 6822K->6816K(8704K), 0.0011019 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 32K->0K(1536K)] [ParOldGen: 6784K->5248K(7168K)] 6816K->5248K(8704K), [Metaspace: 3295K->3295K(1056768K)], 0.0068286 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 5248K->5248K(9216K), 0.0011081 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 5248K->5226K(7168K)] 5248K->5226K(9216K), [Metaspace: 3295K->3295K(1056768K)], 0.0100162 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2048K, used 80K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 1024K, 7% used [0x00000000ffd00000,0x00000000ffd14098,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 7168K, used 5226K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 72% used [0x00000000ff600000,0x00000000ffb1abb0,0x00000000ffd00000)
 Metaspace       used 3327K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 362K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOfRange(Arrays.java:3664)
	at java.lang.String.<init>(String.java:207)
	at java.lang.StringBuilder.toString(StringBuilder.java:407)
	at src.Test1.main(Test1.java:13)

11. 小结:堆空间分代思想

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

12. 内存分配策略

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

13. 为对象分配内存:TLAB

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

14. 小结:堆空间的参数设置

  • 官网说明:
    https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
  • -xx:+PrintFlagsInitial : 查看所有的参数的默认初始值
  • -xX:+PrintFlagsFinal :查看所有的参数的最终值 (可能会存在修改不再是初始值)
  • 具体查看某个参数的指今:
    jps: 查看当前运行中的进程
    jinfo -flag SurvivorRatio 进程id
  • -xms:初始堆空间内存(默认为物理内存的1/64)
  • -Xmx:最大堆空间内存(默认为物理内存的1/4)
  • -Xmn:设置新生代的大小。(初始值及最大值)
  • -xX:NewRatio:配置新生代与老年代在堆结构的占比
    在这里插入图片描述
    在这里插入图片描述

15. 通过逃逸分析看堆空间的对象分配策略

省流:


总结:如何快速的判断是否发生了逃逸分析,
大家就看new的对象实体是否有可能在方法外被调用


//发生了逃逸,不建议这样写(无法使用栈上分配)
public StringBuffer method1(){
	StringBuffer str = new StringBuffer();
	return str;
}

//未发生逃逸,建议这样写以避免一些GC
public String method1(){
	StringBuffer str = new StringBuffer();
	return str.toString();
}

结论:
开发中能使用局部变量的,就不要使用在方法外定义

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

16. 代码优化之栈上分配

在这里插入图片描述
栈上分配:
在这里插入图片描述
同步省略:
在这里插入图片描述
在这里插入图片描述
分离对象或标量替换:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值