【进阶】JVM篇

为什么学习jvm

1、面试的需要

学过java的程序员对jvm应该不陌生,程序员为什么要学习jvm呢?其实不懂jvm也可以照样写出优质的代码,但是不懂jvm会被大厂的面试官虐的体无完肤。

2、高级程序员需要了解

jvm作用

jvm负责把编译后的字节码转换为机器码

jvm内部构造

1.类加载部分:负责把硬盘上字节码加载到内存中(运行时数据区)

2.运行时数据区:负责存储运行时产生的各种数据 类信息,对象信息,方法信息......

3.执行引擎:负责将字节码转为机器码

4.本地方法接口:调用本地方法, Object类中的 hashCode()--拿对象的内存地址

public native int hashCode();

private native int read0() throw IOEception;

垃圾回收部分

jvm类加载系统

1.类加载子系统概述

类加载器子系统负责从文件系统或者网络中加载class文件,类加载系统只负责class文件的加载,至于它是否可以运行,则由执行引擎决定。

加载的类信息存放于一块称为方法区的内存空间

类加载系统,负责将硬盘上的字节码文件加载到jvm中,生成类的Class对象,存储在方法区。

类就是一个模板

2、类加载过程

1.加载

以二进制字符流进行读取

在内存中为类生成Class对象

2.链接

·验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致;

·准备 为类的静态属性进行初始化的赋值

准备阶段 先赋值为默认0 在初始化阶段赋值为'123'

·解析 把字节码的符号引用 替换成 内存中的直接引用地址

3.初始化

初始化阶段主要是为类中的静态成员进行赋值

因为类加载执行完初始化阶段,才说明类加载完成了。

类在哪些情况下会被加载

调用类中静态成员(变量,方法)

new关键字调用

执行该类的main()

反射加载类 Class class.forName("地址");

子类被加载

类在以下两种情况下,是不会被加载的

复制代码
1.类作为数组类型 Demo[] demo = new Demo[10]; //new的数组对象   不是Demo对象 
​
2.只是访问类中的静态的常量 System.out.println(Demo.P);// 优化 不加载整个类了,只获取到用到的静态常量

类加载器

类加载器就是实际负责读取类的功能

类加载器分类:

站在jvm的角度上,分为

引导类加载器(不是用java写的,是用c/c++),负责读取加载java中底层系统库

java写的类加载器(用来读取我们写的应用程序)

在细分类加载器

1.启动类加载器

C/C++语言实现,负载加载java核心类库(系统库 java.lang)

2.扩展类类加载器

用java语言实现的,继承ClassLoader类,加载jre下面扩展类的 jre/lib/ext 子目录

3.应用程序类加载器

用java语言实现的,继承ClassLoader类,用于加载用户自己定义的类(开发的应用程序).

双亲委派机制

当加载一个类时,总是先让他的父级类加载器去加载,确保把系统中类优先加载,直到父类加载器找不到类时,再逐级向下,让子类加载器加载,

如果子级也找不到,最终抛出类找不到异常

为什么这样做?

防止我们自己写的类替换了系统中的类

如何打破双亲委派机制

自定义类加载器

复制代码
MyClassLoader extends ClassLoader
//重写findClass()

运行时数据区

存储运行时产生的各种数据

程序计数器

程序计数器用来记录每一个线程执行的指令位置,

速度是最快的,是线程私有的(每一个线程都会有一个程序计数器)

此区域不会出现内存溢出,也不会垃圾回收

虚拟机栈

栈是运行的,解决程序方法执行,在虚拟机栈中,运行我们java自己写的方法

调用方法,方法入栈,运行结束出栈(先进后出 栈顶的方法,称为当前栈帧)

一个方法就是一个栈帧,在栈帧中存储局部变量,运行结果......

虚拟机栈也是线程私有的,线程之间互相隔离

栈区域不存在垃圾回收,但是会存在内存溢出问题

栈帧中存储什么内容?

局部变量表 int a=10;

操作数栈(计算过程) int c = a+b;

方法返回地址

本地方法栈

本地方法栈是用来执行调用的本地方法的.

是线程私有的,不会存在垃圾回收,

会出现内存溢出问题

堆概述

堆的作用是用来存储java语言产生的对象的.

是运行时数据区中最大的一块内存空间,空间大小可以设置

堆空间是所有线程共享的.

对空间是垃圾回收的重点区域,堆中没有被使用到的垃圾对象,会被垃圾回收器回收调用

堆空间区域划分

堆分为

新生区(新生代 年轻代)

伊甸园区

幸存者0区

幸存者1区

老年区(老年代)

为什么分区(代)?

可以将不同生命周期的对象存储在不同的区域,针对不同的区域采用不同的垃圾回收算法,使得垃圾回收策略更加优化.

对象创建存储过程

新创建的对象都存储在伊甸园区

当垃圾回收时,将还被使用的对象,转移至某一个幸存者区,将伊甸园区进行清除,

当下一次垃圾回收时,将伊甸园区存储的对象与当前正在使用的幸存者区存活的对象,转移至另一个幸存者区(每一次会空闲一个幸存者区)

当一个对象经历过15次垃圾回收后,仍然存活的话,那么就把该对象移动到老年代,

老年代就比较少的进行垃圾回收,在老年代空间不足时,对老年代会进行垃圾回收,

当回收后,内存仍然不足时,会触发FULL GC(整堆收集 应尽量避免)

当整堆收集后仍然不够使用,那么就会出现内存溢出错误 --OOM

jvm调优

可以根据程序具体的使用场景,对运行时数据区的各种空间大小调整 例如堆,方法区

对垃圾回收器进行选择(根据使用的场景选择单线程的或者是多线程的)

方法区

方法区主要用来存储加载的类 信息

方法区的大小也是可以设置的

方法区也会进行垃圾回收,方法区也可能会出现内存溢出的问题

方法区的垃圾回收

方法区的垃圾回收,是对类信息进行回收的

类信息如果不再被使用,类信息也可以被卸载

卸载条件

该类所产生的对象都不存在了

该类的Class对象,也不再被使用了

加载该类的类加载器也被回收了.

本地方法接口

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

什么是本地方法

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

Object hashCode() 获取对象内存地址 涉及到读取内存

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

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

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

因为java属于应用层语言,有时候,需要对硬件系统资源进行调用

此时就不方便,再一个系统资源不允许应用层程序直接调用

那么就需要通过本地方法 调用操作硬件资源

执行引擎

1.执行引擎是java虚拟机核心的组成部分之一

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

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

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

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

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

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

解释器(解释执行):对字节码逐行进行解释翻译,重复性的代码,也是每次都要解释执行,效率低

编译器(编译执行):对某一段字节码进行整体编译,然后存储起来,以后使用时不再需要编译了,效率高。

编译器会针对执行过程中的热点代码进行编译,并缓存起来

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

程序开始运行时,解释器可以立即发挥作用,投入使用

而编译器虽然执行效果高,但是前期需要对热点代码进行跟踪和编译,需要消耗时间

垃圾回收

什么是垃圾对象?

垃圾是指在运行程序中没有任何引用指向的对象

就是一个对象 不再被任何的引用所指向。

没有任何引用所指向的对象

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

垃圾回收发展

早期c/c++这类语言,内存管理都是手动的,使用时申请,使用完后手动释放

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

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

后来发展为自动回收:

java,,C#...都采用自动垃圾回收

优点:解放了程序员

缺点:会占用一些内存空间(垃圾不是出现后立即回收的),降低了程序员管理内存的能力

哪些区域会出现垃圾回收?

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

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

内存溢出与内存泄漏

内存溢出:内存不够用了

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

案例:单例对象

数据库连接对象,IO流,socket 这些提供close()类

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

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

Stop the world

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

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

垃圾回收阶段算法

1.垃圾标记阶段

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

垃圾标记阶段相关算法

引用计数算法(存在缺陷的,没有被虚拟机所使用的)

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

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

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

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

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

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

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

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

2.类中的静态属性

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

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

对象中的finalize机制

Object类中有一个**finalize()**这个方法是在对象被回收之前,由虚拟机自动调用的,

在对象被回收前,需要执行的一些操作,就可以在此方法中编写

finalize()方法可以在子类中重写

finalize()方法指挥被调用一次(第一次被判定为垃圾,要对其回收,调用finalize(),对象有可能又被引用了,对象就不能被回收,当下一次被判定为垃圾对象时,就不会调用finalize())

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

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

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

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

不可触及的(必死无疑的):被判定为垃圾,且执行过finalize()方法

2.垃圾回收阶段

1.标记-复制算法

将内存可以分为多个较小的块,当发生垃圾回收时,将一个区域中存活的对象复制到另一个区域,

在另一个区域从头开始排列,清除当前垃圾回收的区域

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

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

适合用于新生代

2.标记-清除算法

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

如果能存储下,用新对象直接覆盖垃圾对象即可

存活对象是不发生移动的.

**优点:**不会移动对象

不足:回收后,内存空间碎片化

3.标记-压缩(整理)算法

将存活的对象会移动到内存区域的一端,按顺序排列(压缩),清理边界以外的空间,在标记清除的基础上进行一次内存整理.

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

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

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

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

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

先使用标记-清除,当老年代 空间不足时,或者不能存储一个比较大的对象时,在使用标记-压缩算法

垃圾回收时,根据不同的分区采用不同的回收算法

新生代 : 标记-复制

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

垃圾回收器

什么是垃圾回收器

垃圾回收器,是对垃圾回收过程实践者(落地)

不同的虚拟机中,垃圾回收器种类也是很多的

有哪些垃圾回收器 特点

垃圾回收器分类:

从线程数量上分:

单线程 垃圾回收线程只有一个

多线程 有多个垃圾回收线程

从工作模式上分:

独占式:垃圾回收线程执行时,其他用户线程需要暂停(stop the world)

并发式: 垃圾回收线程和用户线程可以做到并发执行

从分区角度上分:

新生代

老年代

垃圾收集器性能指标:

吞吐量

用户线程暂停时间(重点)

回收时内存开销

Serial 单线程 新生代

Serial Old, 单线程 老年代

ParNew、Parallel Scavenge, 多线程 新生代收集器

Parallel Old, 多线程老年代收集器

CMS, 多线程老年代收集器 (开创了垃圾收集线程与用户线程并发执行的先例)

并发标记清除 收集器

初始标记 --独占执行

并发标记 --并发执行

重新标记 --独占执行

并发清除 --并发执行

G1

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

同时,将新生代和老年代的各个区域,又划分成更小的区域,对每个区域进行跟踪,

优先回收价值高的区域(垃圾多的区域,例如可以把伊甸园区可以分成好几个小的区域)

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

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

设置垃圾回收器
相关推荐
书院门前细致的苹果2 小时前
JVM 全面详解:深入理解 Java 的核心运行机制
java·jvm
稻草人想看远方3 小时前
GC垃圾回收
java·开发语言·jvm
我真的是大笨蛋6 小时前
从源码和设计模式深挖AQS(AbstractQueuedSynchronizer)
java·jvm·设计模式
我真的是大笨蛋8 小时前
G1 垃圾收集器深入解析
java·jvm·笔记·缓存
好多1711 小时前
《JVM如何排查OOM》
开发语言·jvm·python
getdu13 小时前
JVM第一部分
jvm
海梨花14 小时前
字节一面 面经(补充版)
jvm·redis·后端·面试·juc
Mr_Xuhhh18 小时前
项目-sqlite类的实现
java·jvm·sqlite
佛祖让我来巡山1 天前
深入理解Java对象:从创建到内存访问的JVM底层机制
jvm·对象创建过程·对象是如何创建的
用手手打人1 天前
JVM详解(一)--JVM和Java体系结构
jvm