JVM探究
参考狂神说视频
- 请你谈谈对JVM的理解?
- java8虚拟机和之前的变化
- 什么是OOM,什么是栈溢出
- JVM的常用调优参数
- 内存快照如何抓取,怎么分析Dump文件?
- 谈谈JVM中,类加载器你的认识?
1、JVM的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBhXVUIf-1651654461356)(JVM快速入门Img/image-20220313102544250.png)]
2、JVM的体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YLQPeL67-1651654461357)(JVM快速入门Img/image-20220313104217734.png)]
堆中一定会存在垃圾,所谓的jvm调优,99%都是在堆中。
- 不会有垃圾回收
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIWDcj9B-1651654461358)(JVM快速入门Img/image-20220313104519187.png)]
- 一般来说的JVM调优99%指的是:堆
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r98Ljubs-1651654461359)(JVM快速入门Img/image-20220313104715526.png)]
- 方法区是一个特殊的堆;
- 虚拟机栈、本地方法栈、程序技术器不会产生垃圾;
- JVM调优几乎都在调堆(包括方法区)
3、类加载器
3.1作用
加载Class文件
~
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUJAgcp7-1651654461360)(JVM快速入门Img/image-20220313112852606.png)]
1、虚拟机自带的加载器
2、启动类(根)加载器:BootStrapClassLoader:引导类加载器,负责java核心类的加载
又名根类加载器或引导类加载器,负责加载==%JAVA_HOME%\bin==目录下的所有jar包,或者是==-Xbootclasspath==参数指定的路径,例:rt.jar
3、扩展类加载器:ExtensionClassLoader
负责加载%JAVA_HOME%\bin\ext目录下的所有jar包,或者是java.ext.dirs参数指定的路径
4、应用程序加载器:AppClassLoader:应用类加载器 负责加载我们写的一些类
又名应用类加载器,负责加载用户类路径上所指定的类库,如果应用程序中没有自定义加载器,那么次加载器就为默认加载器
3.2 类加载器的流程(三个阶段):
Class Loader加载过程:
1.加载阶段
将编译好的class文件加载到内存中(方法区),然后会生成一个代表这个类的Class对象。
2.链接阶段
会为静态变量分配内存并设置默认值。
3.初始化阶段
执行类构造器()进行初始化赋值
4、双亲委派机制
双亲委派机制
- 类加载器收到加载请求
- 把请求委托给父类加载器,
如果父类加载器还存在其父类加载器
,则进一步向上委托,最终将到达顶层的启动类加载器 - 如果父类可以完成加载任务,就成功返回
- 如果完不成,子加载器才会尝试自己去加载
优点:避免重复加载 + 避免核心类篡改
接下来举个例子:
大家所熟知的String类,直接告诉大家,String默认情况下是启动类加载器进行加载的。假设我也自定义一个String,并且制定加载器为自定义加载器。现在你会发现自定义的String可以正常编译,但是永远无法被加载运行。
这是因为申请自定义Object加载时,总是启动类加载器,而不是自定义加载器,也不会是其他的加载器。
public class Car {
/*
双亲委派机制:安全
1. APP-->EXC-->BOOT(最终执行) 向上委托
//BOOT 向下指派
//EXC
//APP
*/
public static void main(String[] args) {
Car car1 = new Car();//实例化
// Car car2 = new Car();
// Car car3 = new Car();
Class<? extends Car> aClass1 = car1.getClass();//实例化对象获得class对象
ClassLoader classLoader = aClass1.getClassLoader();//
System.out.println(classLoader); //AppClassLoader
System.out.println(classLoader.getParent()); //ExtClassLoader jre\lib\ext
System.out.println(classLoader.getParent().getParent()); //null (1.不存在) 2.java程序获取不到 rt.jar 获取得到的是null由于根类加载器是C/C++写的所以获取不到
}
}
1.类加载器收到类加载的请求
2.将这个请求向上委托给父类加载器去完成,一 直向上委托,知道启动类加载器
3.启动加载器检查是否能够加载当前这个类,能加载就结束, 使用当前的加载器,否则, 抛出异常,通知子加载器进行加载
4.重复步骤3
Class Not Found异常就是这么来的
Java早期的名字:C+±-
Java = C++:去掉繁琐的东西,指针,内存管理~
5、沙箱安全机制(了解)
Java安全模型的核心就是Java沙箱(sandbox) , 什么是沙箱?沙箱是一个限制程序运行的环境。
沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。
沙箱主要限制系统资源访问,那系统资源包括什么? CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。
所有的Java程序运行都可以指定沙箱,可以定制安全策略。
在Java中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱Sandbox)机制。如下图所示JDK1.0安全模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rboE13tE-1651654461364)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pfX2NzZG4=,size_16,color_FFFFFF,t_70%23pic_center.jpeg)]
JDK1.0中如此严格的安全机制也给程序的功能扩展带来障碍,比如用户希望远程代码希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的java1.1中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFHUNCaP-1651654461365)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pfX2NzZG4=,size_16,color_FFFFFF,t_70#pic_center.jpeg)]
在Java JDK1.2版本中,再次改进了安全机制,增加了代码签名,不论是本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中不同权限的运行空间,来实现差异化的代码执行权限控制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QXlJpb5O-1651654461367)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pfX2NzZG4=,size_16,color_FFFFFF,t_70#pic_center.jpeg)]
当前最新的安全机制实现,即JDK1.6时期,则引入了域 (Domain) 的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如下图所示
组成沙箱的基本组件:
1.字节码校验器(bytecode verifier) :确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。
2.类裝载器(class loader) :其中类装载器在3个方面对Java沙箱起作用
- 它防止恶意代码去干涉善意的代码;
- 它守护了被信任的类库边界;
- 它将代码归入保护域,确定了代码可以进行哪些操作。、
https://blog.csdn.net/qq_30336433/article/details/83268945
6、Native(唬住)
认识 native 即 JNI,Java Native Interface
程序中使用:private native void start0()
;
- 凡是带了native关键字的,说明java的作用范围达不到了,去调用底层c语言的库!
- 会进入本地方法栈,然后去调用本地方法接口将native方法引入执行
- 本地方法栈(Native Method Stack)
内存区域中专门开辟了一块标记区域: Native Method Stack,负责登记native方法,在执行引擎( Execution Engine )执行的时候通过本地方法接口(JNI)加载本地方法库中的方法
本地方法接口(JNI,java native interface)
本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序, Java在诞生的时候是C/C++横行的时候,想要立足,必须有调用C、C++的程序,然后在内存区域中专门开辟了一块标记区域:
Native Method Stack,负责登记native方法,在执行引擎( Execution Engine )执行的时候通过本地方法接口(JNI)加载本地方法库中的方法
package com.kuan;
public class native_demo {
public static void main(String[] args) {
new Thread(()->{
},"my thread name").start();
}
//native:凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层C语言的库!
//进入本地方法栈
//调用本地方法接口 JNI
//JNI的作用:扩展java的使用,融合不同的编程语言为java所用! 最初:C/C++
//它在内存区域中专门开辟了一块标记区域:Native Method Stack 登记native方法
//在最终执行的时候,加载本地方法库中的方法通过JNI
//java程序驱动打印机,管理系统,掌握即可,在企业应用中较为少见!
private native void hello();
}
7、PC寄存器
程序计数器:Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针, 指向方法区中的方法字节码(用来存储指向像一条指令的地址, 也即将要执行的指令代码,在执行引擎读取下一条指令, 是一个非常小的内存空间,几乎可以忽略不计
8、方法区
方法区是被所有线程共享,存储所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间。
静态变量(static)、常量(final)、类信息(构造方法、接口定义)(Class)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
static、final、class、常量池
9、栈
栈:后进先出,每个线程都有自己的栈,栈内存主管程序的运行,生命周期和线程同步,线程结束,栈内存也就是释放。
对于栈来说,不存在垃圾回收问题,一旦线程结束,栈就结束.
栈内存中运行:8大基本类型+对象引用+实例的方法.
栈运行原理:栈桢
栈满了:StackOverflowError
队列:先进先出(FIFO:First Input First Output)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUtDXYJC-1651654461368)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pfX2NzZG4=,size_16,color_FFFFFF,t_70%23pic_center.png)]
10、三种JVM
- Sun公司 Hotspot Java HotSpot™ 64-Bit Server VM (build 25.211-b12, mixed mode)
- BEA JRockit
- IBM J9VM
11、堆 heap
一个JVM只有一个堆而且堆内存的大小是可以调节的。
类加载器读取类文件后,一般会把类、方法、常量、我们所有引用类型的**真实对象**放入堆中
堆内细分为三个区域:
- 新生区(伊甸区)
- 伊甸园区:所有对象都在伊甸园区new出来
- 幸存0区和幸存1区:轻GC之后存下来的
- 老年区
- 多次轻GC存活下来的对象放在老年区
- 真理:GC垃圾回收主要在新生代和老年代;经过研究,99%的对象都是临时对象!
- 永久区
- jdk1.6之前,持久区,常量池在方法区;
- jdk1.7 :持久区,但是慢慢的退化了,去永久化,常量池在堆中
- jdk1.8之后,无持久区,常量池在元空间
注意:
元空间:逻辑上存在,物理上不存在 ,因为:存储在本地磁盘内,不占用虚拟机内存
默认情况下,JVM使用的最大内存为电脑总内存的四分之一,JVM使用的初始化内存为电脑总内存的六十四分之一.
JVM堆内存划分如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4xqwo4n-1651654461370)(JVM快速入门Img/image-20220315162222904.png)]
- JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
- 年轻代又分为( 伊甸圆区)Eden和Survivor(幸存者)区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
- 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
- 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
12、新生区
- 类:诞生和成长的地方,甚至死亡!
- 伊甸园区:所有对象都在伊甸园区new出来
- 幸存者(0,1)
13、老年区
多次轻GC存活下来的对象放在老年区
真理:GC垃圾回收主要在新生代和老年代;经过研究,99%的对象都是临时对象!
14、永久区
- jdk1.6之前,持久区,常量池在方法区;
- jdk1.7 :持久区,但是慢慢的退化了,去永久化,常量池在堆中
- jdk1.8之后,无持久区,常量池在元空间
注意:
元空间:逻辑上存在,物理上不存在 ,因为:存储在本地磁盘内,不占用虚拟机内存
默认情况下,JVM使用的最大内存为电脑总内存的四分之一,JVM使用的初始化内存为电脑总内存的六十四分之一.
总结:
- 栈:基本类型的变量,对象的引用变量,实例对象的方法
- 堆:存放由new创建的对象和数组
- 方法区:Class对象,static变量,常量池(常量)
15、堆内存调优
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uJ9gcfXy-1651654461371)(JVM快速入门Img/image-20220315172608870.png)]
元空间在逻辑上存在于堆中,当时在物理上并不存在
证明:如下例(新生代+老年代=总内存)
package com.kuan;
import java.util.Random;
public class heap_demo {
static String a="12345678";
public static void main(String[] args) {
while (true){
a=a+new Random().nextInt(888888888)+new Random().nextInt(999999999);
}
/*
Exception in thread "main" java.lang. OutOfMemoryError : Java heap space OOM
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at com.kuan.heap_demo.main(heap_demo.java:9)
Process finished with exit code 1
PSYoungGen total 1024K, used 491K [0x00000000ffe80000, 0x0000000100000000, 0x0000000100000000)
eden space 512K, 96% used [0x00000000ffe80000,0x00000000ffefae90,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 512K, used 474K [0x00000000ffe00000, 0x00000000ffe80000, 0x00000000ffe80000)
object space 512K, 92% used [0x00000000ffe00000,0x00000000ffe76ab8,0x00000000ffe80000)
Metaspace used 3575K, capacity 4502K, committed 4864K, reserved 1056768K
class space used 392K, capacity 394K, committed 512K, reserved 1048576K
491K+474K~1024k
*/
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TrcBxit6-1651654461372)(JVM快速入门Img/image-20220315172745522.png)]
package com.kuan;
public class heap_demo02 {
public static void main(String[] args) {
//返回虚拟机试图使用的最大内存
long max = Runtime.getRuntime().maxMemory();
//返回jvm‘的初始化内存
long total = Runtime.getRuntime().totalMemory();
System.out.println("max"+max+"字节"+(max/1024/1024)+"MB");
System.out.println("total"+max+"字节"+(total/1024/1024)+"MB");
// 默认情况下:分配的总内存 是电脑内存的四分之一,而初始化的内存: 1/64
//OOM
//1.尝试扩大堆内存看结果
//2.分析内存,看一下那个地方出现了问题(专业工具)
//-Xms1024m -Xmx1024m -XX:+PrintGCDetails
// 年轻代+永久代 = 981.5M
}
}
使用JPofiler工具分析OOM原因
在一个项目重突然出现了OOM故障,那么该如何报错?
1.尝试扩大堆内存看结果
2.分析内存,看一下那个地方出现了问题(专业工具)
- 能够看到代码第几行出错:内存快照分析工具,MAT、Jprofiler
- Debug,一行行分析代码!
MAT,Jprofiler作用
- 分析Dump内存文件,快速定位内存泄露;
- 获得堆中的数据
- 获得大的对象~
- …
安装Jprofiler
-
下载Jprofiler https://www.ej-technologies.com/download/jprofiler/version_92
-
安装Jprofiler (下一步即可)
-
安装完成后,需要在IDEA中安装插件(重启)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ISbF5pl-1651654461372)(JVM快速入门Img/image-20220315174038277.png)]
-
添加参数运行程序:
-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError
当出现OOM错误,会生成一个dump文件(进程的内存镜像)
在项目目录下找到dump文件,双击打开
可以看到什么占用了大量的内存
这里可以看到哪一行代码出现问题
package com.kuan;
import java.util.ArrayList;
public class heap_demo03 {
byte[] arr = new byte[1 * 1024 * 1024]; // 1m
/*
* Xms 设置初始化内存分配大小 1/64
* Xmx 设置最大分配内存,默认1/4
* -XX:+PrintGCDetails //打印GC垃圾回收信息
* -XX:+HeapDumpOnOutOfMemoryError //oom DUMP
* -Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError
*/
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
int count = 0;
try {
while (true) {
list.add(new heap_demo03());
count++;
}
} catch (OutOfMemoryError error) {
System.out.println("count:" + count);
error.printStackTrace();
}
}
/*
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid18080.hprof ...
Heap dump file created [1455996 bytes in 0.009 secs]
count:0
java.lang.OutOfMemoryError: Java heap space
at com.kuan.heap_demo03.<init>(heap_demo03.java:6)
at com.kuan.heap_demo03.main(heap_demo03.java:20)
*/
}
16、GC(垃圾回收器)
GC的作用区域:对、方法区
JVM在进行垃圾回收的时候,并不是对这三个区域统一回收,大部分时候,回收都是新生代~~
- 新生代
- 幸存区
- 老年区
GC两种类型:轻GC、重GC
GC题目:
JVM的内存模型和分区~详细到每个区放什么?
https://www.cnblogs.com/infinity-zhang/p/13378598.html
堆里面的分区有哪些?Eden、form、to、老年区,说说他们的特点!
https://blog.csdn.net/Soldier49Zed/article/details/102755507
GC的算法有哪些?标记清除法、标记整理法、复制算法、引用计数法,怎么用的?
https://blog.csdn.net/lingbo229/article/details/82586822
轻GC和重GC分别在什么时候发生?
https://blog.csdn.net/lingbo229/article/details/82586822
引用计数法:
复制算法:
红色是标记的非活动对象,绿色是活动对象。
- 将内存按容量划分为两块,每次只使用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不用考虑内存碎片问题,简单高效。缺点需要两倍的内存空间。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3SI1Dqb-1651654461374)(JVM快速入门Img/image-20220316161650440.png)]
- 好处:没有内存的碎屏
- 缺点:浪费了内存空间:多了一半空间永远是空(to)。假设对象100%存活(极端情况)–》OOM
**复制算法最佳使用场景:对象存活度较低的时候;**新生区
标记-清理:
GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。同时会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09T0F09N-1651654461375)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NDQxNDY2,size_16,color_FFFFFF,t_70.png)]
优点:不需要额外的空间。
缺点:两次扫描,严重浪费时间,会产生内存碎片,
标记-整理:
也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题,但也多了一个移动成本。
一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清理或者标记-整理算法来进行回收。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ds0hZpvk-1651654461375)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NDQxNDY2,size_16,color_FFFFFF,t_70.png)]
13.分代回收策略
- 绝大多数刚刚被创建的对象会存放在Eden区
- 当Eden区第一次满的时候,会触发MinorGC(轻GC)。首先将Eden区的垃圾对象回收清除,并将存活的对象复制到S0,此时S1是空的。
- 下一次Eden区满时,再执行一次垃圾回收,此次会将Eden和S0区中所有垃圾对象清除,并将存活对象复制到S1,此时S0变为空。
- 如此反复在S0和S1之间切换几次(默认15次)之后,还存活的对象将他们转移到老年代中。
- 当老年代满了时会触发FullGC(全GC)
MinorGC
- 使用的算法是复制算法
- 年轻代堆空间紧张时会被触发
- 相对于全收集而言,收集间隔较短
FullGC
- 使用的算法一般是标记压缩算法
- 当老年代堆空间满了,会触发全收集操作
- 可以使用 System.gc()方法来显式的启动全收集
- 全收集非常耗时
17、JMM(Java内存模型)
1、什么是JMM
2、它是干什么的?
3、如何学习
JMM :Java 内存模型
【JMM】(Java Memory Model的缩写)允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员使用了volatile或synchronized明确请求了某些可见性的保证。
作用:缓存一致性协议,用于定义数据读写的规则(遵守)
JMM定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QVBFRtyo-1651654461376)(JVM快速入门Img/image-20220316163617485.png)]
解决共享对象可见性这个问题:volilate
https://juejin.cn/post/6919350421685288973
BAT 经典面试题:
https://blog.csdn.net/weixin_43759452/article/details/90294670
16、垃圾收集器
https://blog.51cto.com/lizhenliang/2164876?wx=
JMM内存模型三大特性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REucACSH-1651654461377)(JVM快速入门Img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pfX2NzZG4=,size_16,color_FFFFFF,t_70.png)]
1、原子性
使用 synchronized 互斥锁来保证操作的原子性
2、可见性:
- volatile,会强制将该变量自己和当时其他变量的状态都刷出缓存。
- synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。
- final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
3、有序性
- 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 ->最终执行的命令
- 重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性
- 处理器在进行重排时必须考虑数据的依赖性,多线程环境线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的。
18、总结
内存效率: 复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
思考一个问题:难道没有最优算法吗?
答案:没有,没有最好的算法,只有最合适的算法—> GC:分代收集算法
年轻代:
存活率低
复制算法
老年代:
存活率高,区域大
标记-整理 + 标记清除 混合实现
方法:
1、百度
2、思维导图
ps:之后学校
单点登录SSO(架构思想)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bIHvAQ1Y-1651654461377)(JVM快速入门Img/image-20220314103041620.png)]
深入理解虚拟机
垃圾回收器
serial收集器:简单而高效
lock 操作之前,必须把变量值同步回主内存。
3. final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
3、有序性
- 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 ->最终执行的命令
- 重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性
- 处理器在进行重排时必须考虑数据的依赖性,多线程环境线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的。
18、总结
内存效率: 复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
思考一个问题:难道没有最优算法吗?
答案:没有,没有最好的算法,只有最合适的算法—> GC:分代收集算法
年轻代:
存活率低
复制算法
老年代:
存活率高,区域大
标记-整理 + 标记清除 混合实现
方法:
1、百度
2、思维导图
ps:之后学校
单点登录SSO(架构思想)
[外链图片转存中…(img-bIHvAQ1Y-1651654461377)]
深入理解虚拟机
垃圾回收器
serial收集器:简单而高效