目录
[Stop the World](#Stop the World)
[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
**感谢你的阅读与关注,如有问题欢迎探讨!**💓