JVM深入学习(二)

目录

一.本地方法接口

二.执行引擎

三.垃圾回收

什么是垃圾对象?

垃圾回收发展

哪些区域会出现垃圾回收

内存溢出与内存泄漏

[Stop the World](#Stop the World)

四.垃圾回收阶段算法

1.垃圾标记阶段

①引用计数算法

②可达性分析算法(根搜索算法)

finalize()方法对象复活

2.垃圾回收阶段

①标记-复制算法

②标记-清除算法

③标记-压缩(整理)算法

五.垃圾回收器

什么是垃圾回收器?

有哪些垃圾回收期?

垃圾收集器性能指标

[G1(Garbage First)](#G1(Garbage First))

设置垃圾回收器


一.本地方法接口

1.什么是本地方法接口?

本地方法接口是虚拟机中专门用来调用本地方法的接口

2.什么是本地方法?

在java中被native关键字修饰的方法, 没有方法体, 不是用java语言实现的方法,是用c/c++在操作系统底层实现的方法。

如:①Object 类中的 hashCode()方法,获取对象内存地址,涉及到读取内存。

②IO中读文件(输入文件 操作硬盘) read0()方法;

③启动线程:native void start0(); 就是把这个线程注册到操作系统。

3.java中为什么要调用本地方法?

因为java属于应用层语言, 有时候,需要对硬件系统资源进行调用,此时就不方便,而且**系统资源不允许应用层程序直接调用,**那么就需要通过本地方法调用硬件资源。


二.执行引擎

执行引擎是虚拟机核心部件之一,主要作用是将加载到虚拟机中的字节码再次转换为机器码 (字节码并不是系统能够直接执行的)。

执行引擎可以通过解释/编译两种方式将字节码转为机器码。

java程序执行过程中涉及两次编译:

第一次:.java文件(源代码 通过jdk javac调用编译器) -->.class文件, 称为前端编译。

第二次: 通过执行引擎将字节码编译为机器码称为后端编译。

将字节码转为机器码有两种方式:

解释器(解释执行): 对字节码逐行进行解释翻译, 每次执行代码都要解释执行, 效率低。

编译器(编译执行): 编译器会针对执行过程中的热点代码进行编译,并缓存起来, 以后使用时不再需要编译了, 效率高。

为什么要使用解释执行和编译执行并存这样的设计?

程序开始运行时,解释器可以立即发挥作用,响应速度快,省去编译的时间,直接投入使用。

而编译器虽然执行速率高,但是前期需要对热点代码进行跟踪和编译,需要消耗时间。两者并存会大大提高代码的运行速率。


三.垃圾回收

什么是垃圾对象?

一个对象不再被任何的引用所指向,没有任何引用指向的对象。

java 复制代码
String s = new String();
s = null;

垃圾对象如果不清理, 新的对象可能没有足够空间,可能会导致内存溢出问题.

垃圾回收发展

早期

c/c++语言,内存管理是手动的, 使用时申请,使用完后手动释放。

优点: 对内存管理更加精确,效率高

缺点: 增加程序员负担, 控制不好,容易出事(忘记释放, 误操作内存空间)

后来发展为自动回收:java,C#...都采用自动垃圾回收

优点: 降低程序员负担

缺点: 会占用一些内存空间(垃圾不是出现后立即回收的,具有周期性)。

哪些区域会出现垃圾回收

堆的对象:频繁回收年轻代, 较少回收老年代

方法区:类信息卸载,整堆收集时,会进行回收 FULL GC

内存溢出与内存泄漏

内存溢出 : 内存不够用了

内存泄漏 : 系统中那些用不到的,但是又不能回收的对象

案例: 单例对象,数据库连接对象,IO流,socket这些提供close()的类。

用完之后,如果没有关闭, 垃圾回收器是不能主动回收这些对象的。

内存泄漏虽然不能直接触发内存溢出, 但是长期有对象不能被回收,也是导致内存溢出的原因之一。

Stop the World

垃圾回收时,会经历两个阶段: 一是标记阶段 ,二是回收阶段。

在标记和回收时,需要暂停用户线程 ,如果不暂停标记和回收,可能会出现错标和漏标


四.垃圾回收阶段算法

1.垃圾标记阶段

将虚拟机中不再被任何引用指向的对象标记出来,在垃圾回收阶段,就会将标记出来对象进行回收。

垃圾标记阶段相关算法

①引用计数算法

(存在缺陷, 没有被虚拟机所使用的)

设计思想: 在对象中维护一个整数计数器变量,当有引用指向对象时,计数器加一,相反就减一(引用断开)。

优点: 设计实现简单, 容易分辩对象是否是垃圾对象。

缺点: 需要维护一个变量存储引用数量, 频繁修改引用计数器变量, 占空间,还耗时。

最重要的是,无法解决循环引用问题

循环引用:A引用B,B引用C,C引用A,若外界P引用为"null" 时,这三者互相引用,计数器为1,所以不会被垃圾回收。

②可达性分析算法 (根搜索算法 )

设计思想: 从一些可以被称为**GCRoots的对象开始向下查找,**只要某一个对象与GCRoots对象有联系的,可以判断对象时被使用的。与根对象引用链没有任何关系的对象,可以视为垃圾对象。

哪些对象可以作为GCRoots(根对象)?

1.虚拟机栈中(被调用的方法)所使用的对象

2.类中的静态属性

3.虚拟机中使用的系统类对象

4.所有被同步锁 synchronized 持有的对象

finalize()方法对象复活

Object类中有一个finalize() 方法,这个方法是在对象被回收之前,由虚拟机自动调用 的,在对象被回收前,需要执行一些操作,就可以在此方法中编写,finalize()方法可以在子类中重写,finalize()方法只会被调用一次 (第一次被判定为垃圾,要对其回收,调用finalize(), 对象有可能又被引用了,对象就不能被回收,当下一次被判定为垃圾对象时,就不会再调用finalize())。

由于finalize()方法存在, 被标记为垃圾的对象,也不是非死不可的。

可以将对象分为三种状态:

可触及 : 被GCRoots引用的,不是垃圾对象。

可复活的: 被判定为垃圾的,但是finalize()方法还没有被调用过的。

不可触及的: 被判定为垃圾的,finalize() 已经被调用过了。

2.垃圾回收阶段

①标记-复制算法

将内存可以分为多个较小的块, 当发生垃圾回收时,**将一个区域中存活的对象复制到另一个区域,**在另一个区域从头开始排列, 清除当前垃圾回收的区域。适合新生代区域。

优点: 清理之后,内存没有碎片。

不足: 回收时,需要移动对象, 所以适合小内存块,而且存活对象少的情况。

②标记-清除算法

将被标记为垃圾的对象地址进行记录,后面如果分配新对象,判断垃圾对象空间是否能够存储下新的对象。

如果能存储下,用新对象直接覆盖垃圾对象即可,存活对象是不发生移动的。

优点: 不会移动对象的。

不足: 回收后,内存中会出现碎片。

③标记-压缩(整理)算法

存活对象会移动到内存区域的一端,按顺序排列(压缩),清理边界以外的空间。

在标记清除的基础上进行一次内存整理

优点: 回收后没有内存碎片。

标记-清除和标记压缩对比

标记-清除: ++不移动++存活对象。

标记-压缩: ++会移动++存活对象。

两者都适合于++老年代对象++回收。

先使用标记-清除,当老年代空间不足,或者不能存储一个较大的对象时,再使用标记压缩算法。
垃圾回收时,根据不同的分区采用不同的回收算法。

新生代: ++标记-复制++

老年代:++标记-清除 / 标记-压缩++


五.垃圾回收器

什么是垃圾回收器?

垃圾回收期,是对垃圾回收过程实践者(落地),不同的虚拟机中,垃圾回收器种类也是很多的。

有哪些垃圾回收期?

垃圾回收器分类:

|---------|-------------------------------------------|
| 从线程数量上分 | 单线程:垃圾回收线程只有一个。 |
| | 多线程:有多个垃圾回收线程。 |
| 从工作模式上分 | 独占式: 垃圾回收线程执行时,其他用户线程需要暂停(stop the world) |
| | 并发式: 垃圾回收线程和用户线程可以做到并发执行 |
| 从分区角度上分 | 新生代垃圾回收器 |
| | 老年代垃圾回收器 |

垃圾收集器性能指标

①吞吐量,②用户线程暂停时间(重点),③回收时内存开销

|--------------------------|---------------------------------------|
| Serial | 单线程,新生代收集器 |
| Serial Old | 单线程,老年代收集器 |
| Parallel Scavenge,ParNew | 多线程,新生代收集器 |
| Parallel Old | 多线程,老年代收集器 |
| CMS(并发标记清除收集器) | 多线程,老年代收集器,开创了垃圾收集线程与用户线程并发执行的先例。 |
[垃圾收集器]

CMS运行方式:

初始标记 -- 独占执行,并发标记 -- 并发执行,重新标记 -- 独占执行,并发清除 -- 并发执行

G1(Garbage First

G1垃圾回收器,继承了CMS中垃圾收集线程和用户线程并行执行的特点,减少了用户线程暂停的时间。

同时,将新生代和老年代的各个区域,又划分成各个更小的区域,对每个区域进行跟踪,优先回收价值高的区域(垃圾多的区域,例如可以把伊甸园区可以分成好几个小的区域)。

提升回收效率,提高了吞吐量。不再区分年轻代和老年代,可以做到对整个堆进行回收。

非常适合服务器端程序,大型项目。

设置垃圾回收器

打印默认垃圾回收器

-XX:+PrintCommandLineFlags -version

打印垃圾回收详细信息

-XX:+PrintGCDetails -version

设置垃圾回收器

-XX:+UseG1GC

**感谢你的阅读与关注,如有问题欢迎探讨!**💓

相关推荐
振鹏Dong19 分钟前
微服务架构及常见微服务技术栈
java·后端
丶小鱼丶20 分钟前
二叉树算法之【中序遍历】
java·算法
摇滚侠1 小时前
Oracle 关闭 impdp任务
java
编程爱好者熊浪2 小时前
RedisBloom使用
java
苇柠2 小时前
Spring框架基础(1)
java·后端·spring
yics.3 小时前
数据结构——栈和队列
java·数据结构
架构师沉默3 小时前
我用一个 Postgres 实现一整套后端架构!
java·spring boot·程序人生·架构·tdd
xiucai_cs3 小时前
布隆过滤器原理与Spring Boot实战
java·spring boot·后端·布隆过滤器
向阳花自开3 小时前
Spring Boot 常用注解速查表
java·spring boot·后端
huan_19933 小时前
通过docker构建一个java镜像
java·docker