JVM学习记录

1.内存与垃圾回收篇

主要用的是jdk8
在这里插入图片描述

1.1 JVM与java体系结构

跨语言的平台在这里插入图片描述
java虚拟机上的多语言混合编程成为主流
JVM的整体结构
JVM整体结构
三大商用虚拟机
Hotspot、JRockit、J9

1.2 类加载子系统

详细版JVM整体结构
在这里插入图片描述

引导类加载器(BootStrap ClassLoader)使用c/c++实现
用来加载java的核心库
不继承java.lang.ClassLoader
拓展类加载器(Extension ClassLoader)使用java编写
派生于ClassLoder类
系统类加载器(AppClassLoader)使用java语言编写
父类加载器为拓展类加载器
该类加载的是程序中默认的类加载器

在这里插入图片描述
双亲委派机制
在这里插入图片描述在这里插入图片描述在这里插入图片描述
沙箱安全机制(保护核心API)

1.3 运行时数据区概述及系统

方法区和堆空间是共用的
程序计数器、本地方法栈和虚拟机栈每个线程都拥有一份
95%的垃圾都来自于堆,5%的垃圾来自方法区
在这里插入图片描述
线程是一个程序里的运行单元
每个线程都和操作系统的本地线程直接映射

1.4 程序计数器(PC寄存器)

用来存储指向下一条指令的地址。执行引擎读取下一条指令。
很小的内存空间
运行速度快
生命周期和线程的生命周期保持一致

常见问题:
使用PC寄存器存储字节码指令地址有什么用呢?
为什么要使用PC寄存器记录当前线程的执行地址?
因为CPU需要不停的切换各个线程,这时候切换回来之后,就得知道接着从哪开始继续执行。必须得记录下来。
程序计数器为什么是每个线程私有的?
多线程看似并行实则并发,为了保存现场。如果CPU一核三线程,看起来是并行,其实是并发。

1.5 Java虚拟机栈

栈是运行时的单位,堆是存储的单位。
栈解决的是程序运行的问题,即程序如何执行。
堆解决的是数据存储的问题,即数据怎么放,放在哪。
每一个线程都有一个Java虚拟机栈,每个Java虚拟机栈中保存着一个个的栈帧,一个栈帧对应着一个Java方法。
java虚拟机栈保存方法的局部变量、部分结果、参与方法的调用和返回。
栈是一种快速有效的分配存储方式,访问速度仅仅次于程序计数器。
对于栈来说,不存在垃圾回收问题的。
在这里插入图片描述

面试题:开发中遇到的异常有哪些?
内存溢出,内存泄漏。

在这里插入图片描述
栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种信息。
一个方法对应一个栈帧。
Java方法有两种返回函数的方式,一种是正常的函数返回,使用return指令,另一种是抛出异常。不管是哪种方式,都会导致栈帧被弹出。
每个栈帧中存储着:局部变量表、操作数栈、动态链接、方法返回地址、一些附加信息
在这里插入图片描述
局部变量表:定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量。
不存在数据安全问题。
局部变量表所需要的容量大小实在编译器确定下来的。
方法嵌套的次数由栈的大小决定。
局部变量表中的变量只在当前方法调用中有效。
静态方法不可以使用this。不能被重写。

非静态方法在局部变量表我们的首位置放的是this

在这里插入图片描述

静态变量和局部变量的对比
局部变量使用前必须显示赋值,成员变量在使用前,都经历默认初始化赋值。

在栈帧中,与性能调优最密切的部分就是局部变量表。
局部变量中的变量是重要的垃圾回收根节点。
操作数栈:在方法执行过程中,根据字节码指令,往栈里写入数据或者提取数据。
主要用于保存计算过程的中间结果,同时作为计算过程中变量的临时存储空间。
用数组来实现的,但是不能用索引,是栈的特点。
在这里插入图片描述
栈顶缓存技术:将栈顶元素全部缓存在屋里CPU的寄存器中,以此降低对内存的读写次数,提高执行引擎的执行效率。

动态链接: 每个栈帧内部都包含一个指向运行时常量池(方法区中的)中该栈所述方法的引用。
动态链接的作用:为了将符号引用转换为调用方法的直接引用。
在这里插入图片描述

为什么需要常池呢?
为了提供一些符号和常量,便于指令的识别。节约内存。

方法的调用:

动态链接和静态链接
动态链接:被调用的方法在编译器无法被确定下来。
静态链接:被调用的方法在编译期可知,并且运行期保持不变。

由于方法的重写导致我们出现了虚方法

java中任何一个方法都具备虚函数的特征,
非虚方法:编译器就确定了具体的调用版本,这个版本在运行期间是不可变的。
		静态方法、私有方法、final方法、实例构造器、父类方法。
虚方法:剩下的就是虚方法。

在这里插入图片描述

绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。
早期绑定和晚期绑定:
早期绑定:被调用的方法在编译期可知,并且运行期保持不变。
晚期绑定:被调用的方法在编译器无法被确定下来,只能在程序运行期间根据实际的类型绑定相关的方法。

动态类型语言和静态类型语言:对于类型的检查是在编译期间还是运行期间。
动态类型语言:java
静态类型语言:python

invokeDynamic指令是Java为了实现动态语言支持而做的一种改进。
虚方法表:重写方法的话,依次向上找不太好,建立一个表,提高性能。
在这里插入图片描述
方法返回地址: 存放该方法PC寄存器的值。
在这里插入图片描述

栈的相关面试题:
1.举例栈溢出的情况。
	StackOverflowError, 往栈里加栈帧,不足的时候就导致栈溢出。
	通过-Xss设置栈的大小。
2.调整栈的大小,就能保证不出现溢出吗?
	不能,本身递归无限循环的话,调整多大都没用。
3.分配的栈内存越大越好吗?
	不是的,资源是有限的,影响其他的线程的空间
4.垃圾回收是否会涉及到虚拟机栈
	不会
5.方法中定义的局部变量是否线程安全
	内部产生内部消亡则安全,如果作为返回值(逃逸了)就有可能被其他的线程修改。

StringBuilder和StringBuffer区别
在这里插入图片描述

1.6 本地方法接口

本地方法: 一个Native Method就是一个Java调用非Java代码的接口。
在这里插入图片描述
native有方法体,只不过不适用java代码实现的,是用C
abstract没有方法体,它和native不能一起使用。

为什么使用本地方法?
1.有时候Java应用需要与Java外面的环境交互。操作系统硬件交换信息时需要用到c
2.与操作系统交互。操作系统底层是用c写的。JVM毕竟是虚拟机,还需要和真实系统打交道。
3.Sun的解释器是用C写的。
4.但是目前该方法使用的越来越少了,除非是与硬件有关的应用。

1.7 本地方法栈

Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用。
在这里插入图片描述
当某个线程调用一个本地方法的时候,他就进入了一个全新的不受虚拟机限制的世界,他和虚拟机拥有同样的权限。
在这里插入图片描述
不是所有的JVM都有本地方法栈
Sun公司的Hotspot中,将本地方法栈和虚拟机栈合二为一。

1.8 堆(Heap)

一个进程对应一个JVM实例,只有一个运行时数据区
一个进程的方法区和堆是唯一的
一个进程中有多个线程
一个进程中的多个线程共享方法区和堆
Java堆区在JVM启动的时候即被创建,其空间也就确定了。就是JVM要管理的最大的一块内存。堆内存的大小是可以调节的。
堆在物理上不连续,在逻辑上是连续的。
堆中分小块线程私有缓冲区(TLAB),堆是一个全局共享的区域,当多个线程同一时刻操作堆内存分配对象空间时,就需要进行同步,而同步带来的效果就是对象分配效率变差。提高并发。
数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。
在方法结束之后,在堆中的对象不会被马上移除,仅仅在垃圾收集的时候才会被移除。
垃圾回收不可以太频繁,因为垃圾回收的时候需要用户进程stop the world,影响用户进程。
堆,是GC(垃圾收集器)的重点区域。
在这里插入图片描述

 -Xms256m -Xmx256m
 设置初始堆空间大小,设置最大堆空间大小
 通过-Xss设置栈的大小。

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

现代垃圾收集器大部分都是基于分代收集理论设计,堆空间细分为:
新生区+养老区+元空间
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
几乎所有的对象都是在伊甸园区被new出来。
绝大部分的Java对象的销毁都在新生代区进行。

面试题
1.jdk8中,内存结构主要有那些变化
	堆中 永久区---->元空间
	元空间不在虚拟机设置的内存中,而是使用本地内存。

只能用一个,所以不是600M
在这里插入图片描述
YGC/Minor GC
伊甸园区满的时候触发YGC,但是s0满的时候不会触发YGC
YGC之后伊甸园区一定是空的。
在这里插入图片描述

对于幸存者s0、s1:复制之后又交换,谁空谁是to。
关于垃圾回收:频繁在新生区收集,很少早养老区收集,几乎不在永久区/元空间收集。

在这里插入图片描述
根据下图理解GC的过程
在这里插入图片描述
常见的调优工具
在这里插入图片描述
Minor GC、Major GC和Full GC
在这里插入图片描述

面试题:
1.为什么把java堆分代?不分代就不能正常工作了吗?
	不分带也可以工作,主要是为了优化GC性能。
2.堆空间一定都是共享的吗?
	不是,还有TLAB
3.堆是分配对象的唯一选择吗?
	先说不是再说是
	当一个对象在方法中被定义后。对象对象只在方法内部使用,则认为没有发生逃逸。
	如果经过逃逸分析,一个对象并没有逃逸出方法的话,那么就有可能被优化成栈上分配。Hotspot斌没有这么做
	逃逸分析本身需要复杂分析。

在这里插入图片描述
逃逸分析:代码优化
在这里插入图片描述

1.9 方法区

在这里插入图片描述
在这里插入图片描述
方法区的内部结构:类型信息、常量、静态变量、即时编译器的代码缓存等
运行时常量池:是方法区的一部分,运行时常量是相对于常量来说的,它具备一个重要特征是:动态性。Java语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

方法区的演进细节
在这里插入图片描述

永久代为什么要被元空间替代?
	1.永久代设置空间大小是很难确定的
	2.对永久代进行调优是很困难的
StringTable(字符串常量池)为什么从永久代挪到了堆?
	因为永久代回收效率低,开发中有大量的字符串被创建,容易导致永久代内存不足,放到堆里可以及时回收。
静态变量存放在哪?
	对象本身始终在堆存放,不同的是指向这个对象的变量本身存放的位置不同
	局部变量在栈上。成员变量在堆上。静态变量在堆。

在这里插入图片描述
方法区的垃圾收集:
主要就是两部分,运行时常量池中废弃的常量、不再使用的类型。
在这里插入图片描述
在这里插入图片描述
对象的内存布局
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
句柄访问
在这里插入图片描述
直接访问
在这里插入图片描述

1.10 直接内存

直接内存:java堆外、直接向系统申请的内存区间
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.11 执行引擎(翻译官)

JVM的主要任务是装在字节码到其内部,但是字节码不能直接运行在操作系统之上。那么执行引擎的任务就是将字节码指令解释、编译为对应平台上的本地机器指令。
在这里插入图片描述

Hotspot为何解释器和即时编译器并存?
JIT那么快为什么还要解释器呢?
首先,程序启动时解释器可以首先发挥作用,响应速度快,省去不必要的编译时间。
之后,JIT发挥作用,根据热点探测功能。将有价值的字节码编译为本地机器指令,换取更高的程序执行效率。

JIT编译器
前端编译器:.java文件转化为.class文件
后端编译器:把字节码转化为机器码
静态提前编译器(AOT):直接把.java文件编译成本地机器代码的过程

是可以设置只使用解释器模式的

1.12 SpringTable(字符串常量池)

面试题
String的不变性
在这里插入图片描述
字符串常量池不会存储相同字符
JDK8中StringTable设置的最小值是1009
在这里插入图片描述
在这里插入图片描述
改进空间:为stringbuilder添加长度

面试题
在这里插入图片描述在这里插入图片描述在这里插入图片描述

1.13 垃圾回收概述

1.14 垃圾回收相关算法

1.15 垃圾回收相关概念

2.字节码与类的加载

3.性能监控与调优篇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值