JVM笔记

本文详细介绍了Java虚拟机(JVM)中的关键概念,如字节码执行、类加载过程、双亲委派机制、运行时数据区(包括程序计数器、虚拟机栈、堆和方法区)、垃圾回收机制(如引用计数、可达性分析、分代回收算法和各种垃圾回收器)以及内存管理。
摘要由CSDN通过智能技术生成

一、查看字节码

1、字节码指令执行

在这里插入图片描述
iconst_0 取常量池0位置的值放到操作数栈中
istore_1 把操作数栈中的值放到局部变量表1位置中
iload_1 把局部变量表1位置的值放到操作数栈中
iinc 1 by 1 局部变量表1位置的值加1

2、查看字节码文件

1、jclasslib
2、服务器查看 javap
3、运行中查看字节码 软件arthas 的dump命令

二、类的生命周期

在这里插入图片描述

1、加载

类加载器将类的字节码加载进方法区中,生成一个InstanceKlass对象,用来保存类的基本信息、常量池、字段、方法、虚方法表等
在这里插入图片描述
同时虚拟机还会在堆区生成一个Class对象,用来保存类的成员字段、方法和静态字段(jdk1.8后)
在这里插入图片描述
java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
在这里插入图片描述

2、链接

连接分为三阶段:验证、准备、解析。验证是检验魔术、版本号。准备是为静态变量分配空间并设置初始值(被final修饰且=号右边是常数的变量也在这里赋值),解析是将常量池里的符号应用替换为直接引用。

3、初始化

  • 执行静态代码块(仅执行一次)、给静态变量赋值

ps: init方法由构造方法和非静态代码块组成,且非静态代码块先执行。初始化时不会执行init方法。静态变量用final修饰的会在准备阶段进行初始化。

4、什么时候触发类初始化

1、调用一个类的静态方法和静态变量。注意变量是final修饰且右边是常量是不会被触发的。
2、初始化子类前,先初始化父类
3、new一个该类的对象
4、Class.forName(xxx)
5、执行Main方法的当前类

初始化时只会执行静态变量赋值和静态代码块调用,不会执行构造方法和非静态代码块。 并且非静态代码块是在构造方法之前执行的。

三、类加载器

1、双亲委派机制有什么作用?

  • 保证类加载的安全·
  • 避免重复加载

2、说一下双亲委派机制?

在这里插入图片描述

3、如何打破双亲委派机制?

自定义类加载器并重写loadClass方法

四、运行时数据区

运行时数据区分为5部分
在这里插入图片描述

1、程序计数器

记录下一行字节码指令的地址。执行完当前指令后,虚拟机的执行引擎根据程序计数器执行下一行指令。
程序计数器不会发生内存溢出

2、虚拟机栈

虚拟机的栈帧包括局部变量表、操作数栈、帧数据等

  • 局部变量表:运行时存放所有局部变量
  • 操作数栈桢:存放临时变量的一块区域
  • 帧数据:包括动态链接、方法出口、异常表的引用

2.1、局部变量表

栈帧中的局部变量表是一个数组,数组的每个位置称为slot,long和double类型占用两个slot,其他类型都只占用一个slot。
主要内容有:实例方法的this对象、方法参数、方法中定义的局部变量。顺序与定义顺序一致。
在这里插入图片描述
在这里插入图片描述
为了节约空间,局部变量是可以复用的。一旦某个局部变量失效,当前slot就可以再次被使用。
在这里插入图片描述

2.2、设置栈大小

语法:-Xss256k 建议设置成256k,一般情况栈深度也就到几百,不会出现栈溢出。windows:1k~1025m

3、堆

堆用来存放new出来的对象
max默认是系统的1/4,total是1/64。超过上限值会抛出outOfMemory
在这里插入图片描述

3.1、设置堆大小

语法:-Xmx值,-Xms值 (建议Xms和-Xmx设置成一样)
Xmx必须大于2MB,Xms必须大于1MB

3.2、静态变量的存储

静态变量存储在堆的Class对象中
在这里插入图片描述

4、方法区

方法区存放的是:类的元信息、运行时常量池、字符串常量池
JDK1.7及之前方法区放在在堆中的永久代
JDK1.8及之后方法区放在直接内存的元空间
JDK8可以使用-XX:MaxMetaspaceSize=值来设置元空间大小
在这里插入图片描述

4.1、字符串常量池

字符串常量池存储的是代码中定义的常量字符串,比如“345”。
1.7前字符串常量池包含在运行时常量池中在永久代的方法区中。1.7及之后字符串常量池分离出来放在堆中。
在这里插入图片描述

4.2、intern

1.7前intern()会把第一次出现的字符串复制一份放到方法区并返回地址。1.7及之后会把字符串的引用放入字符串常量池。
在这里插入图片描述

五、直接内存

ByteBuffer提供了allocateDirect方法用来操作直接内存
在这里插入图片描述

1、设置直接内存

-XX:MaxDirectMemorySize=大小

六、自动垃圾回收

6.1、方法区回收

6.1.1、类卸载的条件

在这里插入图片描述

6.2、堆区回收

6.2.1、怎么判断对象该被回收
引用计数法:

对象维持一个引用计数器,被引用就加1,引用取消则减1
缺点:要维持一个计数器,消耗性能。可能会导致循环引用。

可达性分析:

如果一个对象到GC root对象是可达的,那么这个对象不可被回收。
在这里插入图片描述

生存或者死亡?

即使在可达性分析算法中不可达的对象,也并非"非死不可"的,这时候他们暂时处在"缓刑"阶段。
要宣告一个对象的真正死亡,至少要经历两次标记过程 : 如果对象在进行可达性分析之后发现没有与GC Roots 相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。
当对象没有覆盖finalize()方法或者finalize()方法已经被JVM调用过,虚拟机会将这两种情 况都视为"没有必要执行",此时的对象才是真正"死"的对象。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个叫做F-Queue的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它(这里所说的执行指的是虚拟机会触发finalize()方法)。
finalize()方法是对象逃脱死亡的最后一次机会,稍后GC将对F-Queue中 的对象进行第二次小规模标记,如果对象在finalize()中成功拯救自己(只需要重新与引用链上的任何一个对象建立起关联关系即可),那在第二次标记时它将会被移除出"即将回收"的集合;
如果对象这时候还是没有逃脱,那基本上它就是真的被回收了。

什么是GC root对象?
1、线程Thread对象,它会去引用方法栈桢中的参数和局部变量

在这里插入图片描述

2、Class对象,Class对象保存着类的字段、方法和静态变量

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

3、监视器对象

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

4、本地方法调用时使用的全局对象

在这里插入图片描述

6.2.2、软引用

在这里插入图片描述

在这里插入图片描述

6.2.3、弱引用

在这里插入图片描述#### 6.2.3、虚引用和终结引用
在这里插入图片描述

七、垃圾回收算法

在这里插入图片描述

标记清除

标记清除算法,分为标记和清除两个阶段,首先根据可达性算法标记出所有存活对象,等标记完毕,回收所有没有被标记的对象。标记清除算法有两个不足之处:一个是效率比较低,二是会产生空间碎片,过多的空间碎片会导致为大对象分配空间时,找不到足够的连续空间,不得不提前发生GC。
在这里插入图片描述

复制算法

复制算法解决了标记清除效率低的问题,复制算法将内存空间分为两半,每次只使用一块内存,当这块内存要进行垃圾回收时,将存活对象复制到另一块内存,清空使用过的内存。这样的好处是,每次都回收整个半区,并且分配空间时不需要当心空间碎片,只需要移动堆顶指针。操作简单吞吐量大。当然也存在内存使用率低的问题。
在这里插入图片描述

标记整理算法

老年代的对象生存率较高,复制算法需要较多的复制操作,效率低。因此老年代用标记整理算法更佳。
标记整理算法分为标记和整理两个阶段,标记所有存活对象,然后将存活对象移动到堆的一端,最后清除端边界外的空间。优点是空间利用率高、不会发生空间碎片。
在这里插入图片描述
在这里插入图片描述

分代回收算法

分代回收算法将堆内存分为年轻代和老年代,年轻代有分为eden、幸存者1区、幸存者2区。
分代回收创建出来的对象,首先会被分配到Eden区,满了会触发young gc, 把存活对象复制到to区,然后清空eden区和from区的对象,最后交换from和to的引用。如果young gc后to区内存依然不足,对象将进入老年代。年龄达到15也会进入老年代。在这里插入图片描述

调整堆大小

在这里插入图片描述

八、垃圾回收器

垃圾收集器分为年轻代和老年代
Serial+Serial Old
ParNew+CMS
Parallel Scavenge+Parallel Old
g1
在这里插入图片描述

Serial垃圾回收器

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

ParNew回收器

ParNew是Serial的多线程版本,使用的是复制算法,一般和CMS配合使用。

CMS垃圾收集器

CMS垃圾回收器是多线程的老年代的垃圾回收器。它的目标是获取最短的回收停顿时间。它是基于标记清除算法实现的,分为四个步骤:
初始标记:只标记GC root的直接引用
并发标记:根据直接引用标记引用链上的对象
重新标记:标记并发标记过程中用户创建的存活对象
并发清除
由于并发标记和并发清除这两个阶段的时间最长,而且用户可以在这两阶段同时执行,所以可以获得较短的停顿时间。
缺点:浮动垃圾和空间碎片,空间不足会启用Serial Old来回收
在这里插入图片描述在这里插入图片描述

Parallel Scavenge垃圾收集器

Parallel Scavenge是一个多线程的年轻代垃圾回收器,可以设置最大停顿时间和吞吐量大小。适合不用和用户交互的后台任务。parallel s和ParNew最大的区别是它有一个自适应的调整策略,可以动态的调整新生代大小、Eden和Survivor区的比例、晋升老年代的年龄等。
在这里插入图片描述
在这里插入图片描述

九、未完待续。。。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜里都傻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值