①获取类的全限定类名,把class文件转为二进制流
②把二进制中类的概述信息转入方法区中,如:创建时间、版本
③将java.lang.Class对象存入堆中

①验证:验证被加载类的正确性:如文件的格式,元数据等
②准备:在方法区中为静态变量分配空间,并设置初始值
③解析:把类的符号引用转为直接引用
为类的静态变量设置默认值,执行静态代码块
启动类加载器:主要负责加载JAVA的一些核心类库,主要是位于
拓展类加载器: 主要加载JAVA中的一些拓展类,位于
应用类加载器:主要用于加载CLASSPATH路径下我们自己写的类是拓展类加载器的子类
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给符类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回。若父类加载器不能完成加载任务,子加载器才会尝试自己去加载

自定义类加载器类,继承ClassLoader类,重写loadClass方法

程序计数器会记录当前线程要执行指令的内存地址,只占用一小部分内存区域,只记录一个地址,所以我们认为程序计数器是不会出现内存溢出问题的分区。
Java中有些代码的实现是依赖于其他非Java语言的,本地方法栈存储的是维护非Java语句执行过程中产生的数据,一般我们认为本地方法栈不会出现内存问题
主要是用来存储基本数据类型,同时也存储对象类型的引用,引用地址占4个字节


a=a+1要在操作数栈中进行运算,而a++只需要在局部变量中运算即可
局部变量:
存放当前方法的局部变量,基本数据类型存值i,引用数据类型存堆内存地址
操作数栈:
对方法中的变量提供技术的区域
常量数据的引用:
常量数据会存放到方法区的常量池中,不管是基本数据类型还是引用数据类型都会存放常量池的地址
方法返回值的地址:
方法返回数据会存到计算机内存的寄存器中

设置栈的深度

在java8之后,我们把方法区称为元空间,方法区在逻辑上属于堆的一部分,但在一些具体机制和堆有所区别,如:一些JVM的方法区是可以不进行垃圾回收的,关闭JVM时才会释放方法区内存,所以方法去还有一个别名为非堆
方法区会存储类信息、静态变量、常量(jdk8之后不存放字符串常量)
如果加载大量class文件,也会造成方法区内存溢出,如一个tomcat运行20~30个项目

对象头
MarkWord:一系列标记位(哈希码,分代年龄、锁状态标记),在64位系统中占8位
ClassPoint:对象对应的类信息的内存地址,在64位系统中占8个字节
Length:数组对象特有,表示数组长度,占4字节
实时数据
包含了对象的所有成员变量,大小有各个变量类型决定
封装填充
为了保证对象的大小为8字节的整数倍
如果对象只创建不回收,会造成堆内存溢出异常

老年代:
对象会优先分配到新生代内存中,每次GC后没有回收的对象年龄+1,年龄到15还没有被后手,对象会存放到老年代内存中,如果对象较大,超过新生代内存的一半,对象也会存放到老年代区域
新生代:
为了减少young垃圾回收后的空间碎片,新生代又分为了Eden区和两个Survivor区,且始终有一个Suvivor区保持闲置,对象会先存放到Eden区中,Eden区空间满了之后会进行young区的垃圾回收,之后将young区所有存活的对象复制到闲置的Suvivor区中,并且清空Eden区和正在使用Suvivor区
YoungGC
新生代区域的垃圾回收称为YoungGC,也叫MinorGC,Eden区满后会触发YoungGC
OldGC
老年代区域的垃圾回收称之为OldGC,也叫MajorGC,OldGC非常浪费性能,所以我们的JVM调优要尽可能减少OldGC的次数,OldGC往往伴随着YoungGC
1.Survivor区空间并不大,如果满了怎么办?
(1)一般情况下GC回收95%的对象,且超过15次GC的对象会放到Old区,所以Survivor区不容易满
(2)如果Suvivor区满了,会触发担保机制,提前将对象存入old区
2.为什么需要Suvivor区?
为了减少垃圾回收带来的空间碎片,空间碎片过多会频繁出发YoungGC
3.为什需要俩个Suvivor区?
为了减少Suvivor区的空间碎片,如果只存在一个Suvivor区,则对象向从Eden区进入到Suvivor区,区满进行GC后,可能会产生空间碎片,采用两个Suvivor区,两区可以交替回收整理,减少空间碎片
VisualVM在Java8以及之前都是不用下载的,在下载的java路径下的bin中

版本在8之后的得自行安装
下载链接:https://visualvm.github.io/
安装一路无脑下一步
下面就是初始页面

【一个简单的循环创建对象的方法】

-Xms10M -Xmx10M
设置成这样子可以更加直观的看到,新生代和年老代的变化,



如果要操作对象,必须通过引用来进行,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到,那么这个对象就成为可被回收的对象了,这种方法实现简单,效率较高,但是它无法解决循环引用问题,因此在java中并没有采用这种方式(pyhon使用的是引用计数法)
循环引用问题:是指创建了两个属于同一个类的对象,这两个对象之间相互引用,如下图object,当我的object1和object2 不在指向那两个对象时,正常情况下,我们应该要把其回收掉,但是它们两个还有引用,所以采用引用计数法无法将他们回收!

以一个GC ROOT 对象作为起点进行搜索,如果在GC ROOT和对象之间没有可达路径,则称该对象是不可达的。
GC ROOT对象




垃圾回收器是对垃圾回收算法的实现,JVM中提供了很多垃圾回收器,我们通过以下式子来评判垃圾回收器的好坏
垃圾回收器的执行效率= 吞吐量/停顿时间
吞吐量 = 用户代码执行时间/(用户代码执行时间+停顿时间)



CMS(并发标记扫描)收集器是并发收集器,是基于标记-清理算法进行垃圾后手,用于OldGC
优点: 并发收集,低停顿
缺点: 会产生大量空间碎片,停顿时间虽然短但是不可控
为什么CMS收集器不进行并发的初始标记?
因为标记垃圾所需要耗费的时间较少,没有必要再并发开启多线程,这样子会使程序更加耗费资源



ZGC从JDK11开始支持,目前还是实验性版本,原理类似G1,是目前效率最高的垃圾收集器,平均暂停时间为0.05毫秒