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

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

相关推荐
gentle_ice9 分钟前
leetcode——矩阵置零(java)
java·算法·leetcode·矩阵
whisperrr.1 小时前
【JavaWeb06】Tomcat基础入门:架构理解与基本配置指南
java·架构·tomcat
火烧屁屁啦2 小时前
【JavaEE进阶】应用分层
java·前端·java-ee
m0_748257462 小时前
鸿蒙NEXT(五):鸿蒙版React Native架构浅析
java
我没想到原来他们都是一堆坏人2 小时前
2023年版本IDEA复制项目并修改端口号和运行内存
java·ide·intellij-idea
Suwg2094 小时前
【由浅入深认识Maven】第1部分 maven简介与核心概念
java·maven
花心蝴蝶.4 小时前
Spring MVC 综合案例
java·后端·spring
组合缺一6 小时前
Solon Cloud Gateway 开发:Helloword
java·gateway·solon
奕辰杰9 小时前
关于使用微服务的注意要点总结
java·微服务·架构
m0_7482302110 小时前
适用于IntelliJ IDEA 2024.1.2部署Tomcat的完整方法,以及笔者踩的坑,避免高血压,保姆级教程
java·tomcat·intellij-idea